diff options
Diffstat (limited to 'includes/util')
-rw-r--r-- | includes/util/Parsedown.php | 1994 | ||||
-rw-r--r-- | includes/util/agewarning.inc | 35 | ||||
-rw-r--r-- | includes/util/banner.inc | 480 | ||||
-rw-r--r-- | includes/util/bitset.inc | 21 | ||||
-rw-r--r-- | includes/util/functions.inc | 551 | ||||
-rw-r--r-- | includes/util/homepage.inc | 36 | ||||
-rw-r--r-- | includes/util/language.inc | 3 | ||||
-rw-r--r-- | includes/util/profiles.inc | 97 | ||||
-rw-r--r-- | includes/util/pronouns.inc | 149 | ||||
-rw-r--r-- | includes/util/rainbow.inc | 56 | ||||
-rw-r--r-- | includes/util/random.inc | 15 | ||||
-rw-r--r-- | includes/util/score.inc | 136 | ||||
-rw-r--r-- | includes/util/session.inc | 29 | ||||
-rw-r--r-- | includes/util/short.inc | 55 | ||||
-rw-r--r-- | includes/util/travelling.inc | 62 |
15 files changed, 3719 insertions, 0 deletions
diff --git a/includes/util/Parsedown.php b/includes/util/Parsedown.php new file mode 100644 index 0000000..3e29589 --- /dev/null +++ b/includes/util/Parsedown.php @@ -0,0 +1,1994 @@ +<?php + +# +# +# Parsedown +# http://parsedown.org +# +# (c) Emanuil Rusev +# http://erusev.com +# +# For the full license information, view the LICENSE file that was distributed +# with this source code. +# +# + +class Parsedown +{ + # ~ + + const version = '1.8.0-beta-7'; + + # ~ + + function text($text) + { + $Elements = $this->textElements($text); + + # convert to markup + $markup = $this->elements($Elements); + + # trim line breaks + $markup = trim($markup, "\n"); + + return $markup; + } + + protected function textElements($text) + { + # make sure no definitions are set + $this->DefinitionData = array(); + + # standardize line breaks + $text = str_replace(array("\r\n", "\r"), "\n", $text); + + # remove surrounding line breaks + $text = trim($text, "\n"); + + # split text into lines + $lines = explode("\n", $text); + + # iterate through lines to identify blocks + return $this->linesElements($lines); + } + + # + # Setters + # + + function setBreaksEnabled($breaksEnabled) + { + $this->breaksEnabled = $breaksEnabled; + + return $this; + } + + protected $breaksEnabled; + + function setMarkupEscaped($markupEscaped) + { + $this->markupEscaped = $markupEscaped; + + return $this; + } + + protected $markupEscaped; + + function setUrlsLinked($urlsLinked) + { + $this->urlsLinked = $urlsLinked; + + return $this; + } + + protected $urlsLinked = true; + + function setSafeMode($safeMode) + { + $this->safeMode = (bool) $safeMode; + + return $this; + } + + protected $safeMode; + + function setStrictMode($strictMode) + { + $this->strictMode = (bool) $strictMode; + + return $this; + } + + protected $strictMode; + + protected $safeLinksWhitelist = array( + 'http://', + 'https://', + 'ftp://', + 'ftps://', + 'mailto:', + 'tel:', + 'data:image/png;base64,', + 'data:image/gif;base64,', + 'data:image/jpeg;base64,', + 'irc:', + 'ircs:', + 'git:', + 'ssh:', + 'news:', + 'steam:', + ); + + # + # Lines + # + + protected $BlockTypes = array( + '#' => array('Header'), + '*' => array('Rule', 'List'), + '+' => array('List'), + '-' => array('SetextHeader', 'Table', 'Rule', 'List'), + '0' => array('List'), + '1' => array('List'), + '2' => array('List'), + '3' => array('List'), + '4' => array('List'), + '5' => array('List'), + '6' => array('List'), + '7' => array('List'), + '8' => array('List'), + '9' => array('List'), + ':' => array('Table'), + '<' => array('Comment', 'Markup'), + '=' => array('SetextHeader'), + '>' => array('Quote'), + '[' => array('Reference'), + '_' => array('Rule'), + '`' => array('FencedCode'), + '|' => array('Table'), + '~' => array('FencedCode'), + ); + + # ~ + + protected $unmarkedBlockTypes = array( + 'Code', + ); + + # + # Blocks + # + + protected function lines(array $lines) + { + return $this->elements($this->linesElements($lines)); + } + + protected function linesElements(array $lines) + { + $Elements = array(); + $CurrentBlock = null; + + foreach ($lines as $line) + { + if (chop($line) === '') + { + if (isset($CurrentBlock)) + { + $CurrentBlock['interrupted'] = (isset($CurrentBlock['interrupted']) + ? $CurrentBlock['interrupted'] + 1 : 1 + ); + } + + continue; + } + + while (($beforeTab = strstr($line, "\t", true)) !== false) + { + $shortage = 4 - mb_strlen($beforeTab, 'utf-8') % 4; + + $line = $beforeTab + . str_repeat(' ', $shortage) + . substr($line, strlen($beforeTab) + 1) + ; + } + + $indent = strspn($line, ' '); + + $text = $indent > 0 ? substr($line, $indent) : $line; + + # ~ + + $Line = array('body' => $line, 'indent' => $indent, 'text' => $text); + + # ~ + + if (isset($CurrentBlock['continuable'])) + { + $methodName = 'block' . $CurrentBlock['type'] . 'Continue'; + $Block = $this->$methodName($Line, $CurrentBlock); + + if (isset($Block)) + { + $CurrentBlock = $Block; + + continue; + } + else + { + if ($this->isBlockCompletable($CurrentBlock['type'])) + { + $methodName = 'block' . $CurrentBlock['type'] . 'Complete'; + $CurrentBlock = $this->$methodName($CurrentBlock); + } + } + } + + # ~ + + $marker = $text[0]; + + # ~ + + $blockTypes = $this->unmarkedBlockTypes; + + if (isset($this->BlockTypes[$marker])) + { + foreach ($this->BlockTypes[$marker] as $blockType) + { + $blockTypes []= $blockType; + } + } + + # + # ~ + + foreach ($blockTypes as $blockType) + { + $Block = $this->{"block$blockType"}($Line, $CurrentBlock); + + if (isset($Block)) + { + $Block['type'] = $blockType; + + if ( ! isset($Block['identified'])) + { + if (isset($CurrentBlock)) + { + $Elements[] = $this->extractElement($CurrentBlock); + } + + $Block['identified'] = true; + } + + if ($this->isBlockContinuable($blockType)) + { + $Block['continuable'] = true; + } + + $CurrentBlock = $Block; + + continue 2; + } + } + + # ~ + + if (isset($CurrentBlock) and $CurrentBlock['type'] === 'Paragraph') + { + $Block = $this->paragraphContinue($Line, $CurrentBlock); + } + + if (isset($Block)) + { + $CurrentBlock = $Block; + } + else + { + if (isset($CurrentBlock)) + { + $Elements[] = $this->extractElement($CurrentBlock); + } + + $CurrentBlock = $this->paragraph($Line); + + $CurrentBlock['identified'] = true; + } + } + + # ~ + + if (isset($CurrentBlock['continuable']) and $this->isBlockCompletable($CurrentBlock['type'])) + { + $methodName = 'block' . $CurrentBlock['type'] . 'Complete'; + $CurrentBlock = $this->$methodName($CurrentBlock); + } + + # ~ + + if (isset($CurrentBlock)) + { + $Elements[] = $this->extractElement($CurrentBlock); + } + + # ~ + + return $Elements; + } + + protected function extractElement(array $Component) + { + if ( ! isset($Component['element'])) + { + if (isset($Component['markup'])) + { + $Component['element'] = array('rawHtml' => $Component['markup']); + } + elseif (isset($Component['hidden'])) + { + $Component['element'] = array(); + } + } + + return $Component['element']; + } + + protected function isBlockContinuable($Type) + { + return method_exists($this, 'block' . $Type . 'Continue'); + } + + protected function isBlockCompletable($Type) + { + return method_exists($this, 'block' . $Type . 'Complete'); + } + + # + # Code + + protected function blockCode($Line, $Block = null) + { + if (isset($Block) and $Block['type'] === 'Paragraph' and ! isset($Block['interrupted'])) + { + return; + } + + if ($Line['indent'] >= 4) + { + $text = substr($Line['body'], 4); + + $Block = array( + 'element' => array( + 'name' => 'pre', + 'element' => array( + 'name' => 'code', + 'text' => $text, + ), + ), + ); + + return $Block; + } + } + + protected function blockCodeContinue($Line, $Block) + { + if ($Line['indent'] >= 4) + { + if (isset($Block['interrupted'])) + { + $Block['element']['element']['text'] .= str_repeat("\n", $Block['interrupted']); + + unset($Block['interrupted']); + } + + $Block['element']['element']['text'] .= "\n"; + + $text = substr($Line['body'], 4); + + $Block['element']['element']['text'] .= $text; + + return $Block; + } + } + + protected function blockCodeComplete($Block) + { + return $Block; + } + + # + # Comment + + protected function blockComment($Line) + { + if ($this->markupEscaped or $this->safeMode) + { + return; + } + + if (strpos($Line['text'], '<!--') === 0) + { + $Block = array( + 'element' => array( + 'rawHtml' => $Line['body'], + 'autobreak' => true, + ), + ); + + if (strpos($Line['text'], '-->') !== false) + { + $Block['closed'] = true; + } + + return $Block; + } + } + + protected function blockCommentContinue($Line, array $Block) + { + if (isset($Block['closed'])) + { + return; + } + + $Block['element']['rawHtml'] .= "\n" . $Line['body']; + + if (strpos($Line['text'], '-->') !== false) + { + $Block['closed'] = true; + } + + return $Block; + } + + # + # Fenced Code + + protected function blockFencedCode($Line) + { + $marker = $Line['text'][0]; + + $openerLength = strspn($Line['text'], $marker); + + if ($openerLength < 3) + { + return; + } + + $infostring = trim(substr($Line['text'], $openerLength), "\t "); + + if (strpos($infostring, '`') !== false) + { + return; + } + + $Element = array( + 'name' => 'code', + 'text' => '', + ); + + if ($infostring !== '') + { + /** + * https://www.w3.org/TR/2011/WD-html5-20110525/elements.html#classes + * Every HTML element may have a class attribute specified. + * The attribute, if specified, must have a value that is a set + * of space-separated tokens representing the various classes + * that the element belongs to. + * [...] + * The space characters, for the purposes of this specification, + * are U+0020 SPACE, U+0009 CHARACTER TABULATION (tab), + * U+000A LINE FEED (LF), U+000C FORM FEED (FF), and + * U+000D CARRIAGE RETURN (CR). + */ + $language = substr($infostring, 0, strcspn($infostring, " \t\n\f\r")); + + $Element['attributes'] = array('class' => "language-$language"); + } + + $Block = array( + 'char' => $marker, + 'openerLength' => $openerLength, + 'element' => array( + 'name' => 'pre', + 'element' => $Element, + ), + ); + + return $Block; + } + + protected function blockFencedCodeContinue($Line, $Block) + { + if (isset($Block['complete'])) + { + return; + } + + if (isset($Block['interrupted'])) + { + $Block['element']['element']['text'] .= str_repeat("\n", $Block['interrupted']); + + unset($Block['interrupted']); + } + + if (($len = strspn($Line['text'], $Block['char'])) >= $Block['openerLength'] + and chop(substr($Line['text'], $len), ' ') === '' + ) { + $Block['element']['element']['text'] = substr($Block['element']['element']['text'], 1); + + $Block['complete'] = true; + + return $Block; + } + + $Block['element']['element']['text'] .= "\n" . $Line['body']; + + return $Block; + } + + protected function blockFencedCodeComplete($Block) + { + return $Block; + } + + # + # Header + + protected function blockHeader($Line) + { + $level = strspn($Line['text'], '#'); + + if ($level > 6) + { + return; + } + + $text = trim($Line['text'], '#'); + + if ($this->strictMode and isset($text[0]) and $text[0] !== ' ') + { + return; + } + + $text = trim($text, ' '); + + $Block = array( + 'element' => array( + 'name' => 'h' . $level, + 'handler' => array( + 'function' => 'lineElements', + 'argument' => $text, + 'destination' => 'elements', + ) + ), + ); + + return $Block; + } + + # + # List + + protected function blockList($Line, array $CurrentBlock = null) + { + list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]{1,9}+[.\)]'); + + if (preg_match('/^('.$pattern.'([ ]++|$))(.*+)/', $Line['text'], $matches)) + { + $contentIndent = strlen($matches[2]); + + if ($contentIndent >= 5) + { + $contentIndent -= 1; + $matches[1] = substr($matches[1], 0, -$contentIndent); + $matches[3] = str_repeat(' ', $contentIndent) . $matches[3]; + } + elseif ($contentIndent === 0) + { + $matches[1] .= ' '; + } + + $markerWithoutWhitespace = strstr($matches[1], ' ', true); + + $Block = array( + 'indent' => $Line['indent'], + 'pattern' => $pattern, + 'data' => array( + 'type' => $name, + 'marker' => $matches[1], + 'markerType' => ($name === 'ul' ? $markerWithoutWhitespace : substr($markerWithoutWhitespace, -1)), + ), + 'element' => array( + 'name' => $name, + 'elements' => array(), + ), + ); + $Block['data']['markerTypeRegex'] = preg_quote($Block['data']['markerType'], '/'); + + if ($name === 'ol') + { + $listStart = ltrim(strstr($matches[1], $Block['data']['markerType'], true), '0') ?: '0'; + + if ($listStart !== '1') + { + if ( + isset($CurrentBlock) + and $CurrentBlock['type'] === 'Paragraph' + and ! isset($CurrentBlock['interrupted']) + ) { + return; + } + + $Block['element']['attributes'] = array('start' => $listStart); + } + } + + $Block['li'] = array( + 'name' => 'li', + 'handler' => array( + 'function' => 'li', + 'argument' => !empty($matches[3]) ? array($matches[3]) : array(), + 'destination' => 'elements' + ) + ); + + $Block['element']['elements'] []= & $Block['li']; + + return $Block; + } + } + + protected function blockListContinue($Line, array $Block) + { + if (isset($Block['interrupted']) and empty($Block['li']['handler']['argument'])) + { + return null; + } + + $requiredIndent = ($Block['indent'] + strlen($Block['data']['marker'])); + + if ($Line['indent'] < $requiredIndent + and ( + ( + $Block['data']['type'] === 'ol' + and preg_match('/^[0-9]++'.$Block['data']['markerTypeRegex'].'(?:[ ]++(.*)|$)/', $Line['text'], $matches) + ) or ( + $Block['data']['type'] === 'ul' + and preg_match('/^'.$Block['data']['markerTypeRegex'].'(?:[ ]++(.*)|$)/', $Line['text'], $matches) + ) + ) + ) { + if (isset($Block['interrupted'])) + { + $Block['li']['handler']['argument'] []= ''; + + $Block['loose'] = true; + + unset($Block['interrupted']); + } + + unset($Block['li']); + + $text = isset($matches[1]) ? $matches[1] : ''; + + $Block['indent'] = $Line['indent']; + + $Block['li'] = array( + 'name' => 'li', + 'handler' => array( + 'function' => 'li', + 'argument' => array($text), + 'destination' => 'elements' + ) + ); + + $Block['element']['elements'] []= & $Block['li']; + + return $Block; + } + elseif ($Line['indent'] < $requiredIndent and $this->blockList($Line)) + { + return null; + } + + if ($Line['text'][0] === '[' and $this->blockReference($Line)) + { + return $Block; + } + + if ($Line['indent'] >= $requiredIndent) + { + if (isset($Block['interrupted'])) + { + $Block['li']['handler']['argument'] []= ''; + + $Block['loose'] = true; + + unset($Block['interrupted']); + } + + $text = substr($Line['body'], $requiredIndent); + + $Block['li']['handler']['argument'] []= $text; + + return $Block; + } + + if ( ! isset($Block['interrupted'])) + { + $text = preg_replace('/^[ ]{0,'.$requiredIndent.'}+/', '', $Line['body']); + + $Block['li']['handler']['argument'] []= $text; + + return $Block; + } + } + + protected function blockListComplete(array $Block) + { + if (isset($Block['loose'])) + { + foreach ($Block['element']['elements'] as &$li) + { + if (end($li['handler']['argument']) !== '') + { + $li['handler']['argument'] []= ''; + } + } + } + + return $Block; + } + + # + # Quote + + protected function blockQuote($Line) + { + if (preg_match('/^>[ ]?+(.*+)/', $Line['text'], $matches)) + { + $Block = array( + 'element' => array( + 'name' => 'blockquote', + 'handler' => array( + 'function' => 'linesElements', + 'argument' => (array) $matches[1], + 'destination' => 'elements', + ) + ), + ); + + return $Block; + } + } + + protected function blockQuoteContinue($Line, array $Block) + { + if (isset($Block['interrupted'])) + { + return; + } + + if ($Line['text'][0] === '>' and preg_match('/^>[ ]?+(.*+)/', $Line['text'], $matches)) + { + $Block['element']['handler']['argument'] []= $matches[1]; + + return $Block; + } + + if ( ! isset($Block['interrupted'])) + { + $Block['element']['handler']['argument'] []= $Line['text']; + + return $Block; + } + } + + # + # Rule + + protected function blockRule($Line) + { + $marker = $Line['text'][0]; + + if (substr_count($Line['text'], $marker) >= 3 and chop($Line['text'], " $marker") === '') + { + $Block = array( + 'element' => array( + 'name' => 'hr', + ), + ); + + return $Block; + } + } + + # + # Setext + + protected function blockSetextHeader($Line, array $Block = null) + { + if ( ! isset($Block) or $Block['type'] !== 'Paragraph' or isset($Block['interrupted'])) + { + return; + } + + if ($Line['indent'] < 4 and chop(chop($Line['text'], ' '), $Line['text'][0]) === '') + { + $Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2'; + + return $Block; + } + } + + # + # Markup + + protected function blockMarkup($Line) + { + if ($this->markupEscaped or $this->safeMode) + { + return; + } + + if (preg_match('/^<[\/]?+(\w*)(?:[ ]*+'.$this->regexHtmlAttribute.')*+[ ]*+(\/)?>/', $Line['text'], $matches)) + { + $element = strtolower($matches[1]); + + if (in_array($element, $this->textLevelElements)) + { + return; + } + + $Block = array( + 'name' => $matches[1], + 'element' => array( + 'rawHtml' => $Line['text'], + 'autobreak' => true, + ), + ); + + return $Block; + } + } + + protected function blockMarkupContinue($Line, array $Block) + { + if (isset($Block['closed']) or isset($Block['interrupted'])) + { + return; + } + + $Block['element']['rawHtml'] .= "\n" . $Line['body']; + + return $Block; + } + + # + # Reference + + protected function blockReference($Line) + { + if (strpos($Line['text'], ']') !== false + and preg_match('/^\[(.+?)\]:[ ]*+<?(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*+$/', $Line['text'], $matches) + ) { + $id = strtolower($matches[1]); + + $Data = array( + 'url' => $matches[2], + 'title' => isset($matches[3]) ? $matches[3] : null, + ); + + $this->DefinitionData['Reference'][$id] = $Data; + + $Block = array( + 'element' => array(), + ); + + return $Block; + } + } + + # + # Table + + protected function blockTable($Line, array $Block = null) + { + if ( ! isset($Block) or $Block['type'] !== 'Paragraph' or isset($Block['interrupted'])) + { + return; + } + + if ( + strpos($Block['element']['handler']['argument'], '|') === false + and strpos($Line['text'], '|') === false + and strpos($Line['text'], ':') === false + or strpos($Block['element']['handler']['argument'], "\n") !== false + ) { + return; + } + + if (chop($Line['text'], ' -:|') !== '') + { + return; + } + + $alignments = array(); + + $divider = $Line['text']; + + $divider = trim($divider); + $divider = trim($divider, '|'); + + $dividerCells = explode('|', $divider); + + foreach ($dividerCells as $dividerCell) + { + $dividerCell = trim($dividerCell); + + if ($dividerCell === '') + { + return; + } + + $alignment = null; + + if ($dividerCell[0] === ':') + { + $alignment = 'left'; + } + + if (substr($dividerCell, - 1) === ':') + { + $alignment = $alignment === 'left' ? 'center' : 'right'; + } + + $alignments []= $alignment; + } + + # ~ + + $HeaderElements = array(); + + $header = $Block['element']['handler']['argument']; + + $header = trim($header); + $header = trim($header, '|'); + + $headerCells = explode('|', $header); + + if (count($headerCells) !== count($alignments)) + { + return; + } + + foreach ($headerCells as $index => $headerCell) + { + $headerCell = trim($headerCell); + + $HeaderElement = array( + 'name' => 'th', + 'handler' => array( + 'function' => 'lineElements', + 'argument' => $headerCell, + 'destination' => 'elements', + ) + ); + + if (isset($alignments[$index])) + { + $alignment = $alignments[$index]; + + $HeaderElement['attributes'] = array( + 'style' => "text-align: $alignment;", + ); + } + + $HeaderElements []= $HeaderElement; + } + + # ~ + + $Block = array( + 'alignments' => $alignments, + 'identified' => true, + 'element' => array( + 'name' => 'table', + 'elements' => array(), + ), + ); + + $Block['element']['elements'] []= array( + 'name' => 'thead', + ); + + $Block['element']['elements'] []= array( + 'name' => 'tbody', + 'elements' => array(), + ); + + $Block['element']['elements'][0]['elements'] []= array( + 'name' => 'tr', + 'elements' => $HeaderElements, + ); + + return $Block; + } + + protected function blockTableContinue($Line, array $Block) + { + if (isset($Block['interrupted'])) + { + return; + } + + if (count($Block['alignments']) === 1 or $Line['text'][0] === '|' or strpos($Line['text'], '|')) + { + $Elements = array(); + + $row = $Line['text']; + + $row = trim($row); + $row = trim($row, '|'); + + preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]++`|`)++/', $row, $matches); + + $cells = array_slice($matches[0], 0, count($Block['alignments'])); + + foreach ($cells as $index => $cell) + { + $cell = trim($cell); + + $Element = array( + 'name' => 'td', + 'handler' => array( + 'function' => 'lineElements', + 'argument' => $cell, + 'destination' => 'elements', + ) + ); + + if (isset($Block['alignments'][$index])) + { + $Element['attributes'] = array( + 'style' => 'text-align: ' . $Block['alignments'][$index] . ';', + ); + } + + $Elements []= $Element; + } + + $Element = array( + 'name' => 'tr', + 'elements' => $Elements, + ); + + $Block['element']['elements'][1]['elements'] []= $Element; + + return $Block; + } + } + + # + # ~ + # + + protected function paragraph($Line) + { + return array( + 'type' => 'Paragraph', + 'element' => array( + 'name' => 'p', + 'handler' => array( + 'function' => 'lineElements', + 'argument' => $Line['text'], + 'destination' => 'elements', + ), + ), + ); + } + + protected function paragraphContinue($Line, array $Block) + { + if (isset($Block['interrupted'])) + { + return; + } + + $Block['element']['handler']['argument'] .= "\n".$Line['text']; + + return $Block; + } + + # + # Inline Elements + # + + protected $InlineTypes = array( + '!' => array('Image'), + '&' => array('SpecialCharacter'), + '*' => array('Emphasis'), + ':' => array('Url'), + '<' => array('UrlTag', 'EmailTag', 'Markup'), + '[' => array('Link'), + '_' => array('Emphasis'), + '`' => array('Code'), + '~' => array('Strikethrough'), + '\\' => array('EscapeSequence'), + ); + + # ~ + + protected $inlineMarkerList = '!*_&[:<`~\\'; + + # + # ~ + # + + public function line($text, $nonNestables = array()) + { + return $this->elements($this->lineElements($text, $nonNestables)); + } + + protected function lineElements($text, $nonNestables = array()) + { + # standardize line breaks + $text = str_replace(array("\r\n", "\r"), "\n", $text); + + $Elements = array(); + + $nonNestables = (empty($nonNestables) + ? array() + : array_combine($nonNestables, $nonNestables) + ); + + # $excerpt is based on the first occurrence of a marker + + while ($excerpt = strpbrk($text, $this->inlineMarkerList)) + { + $marker = $excerpt[0]; + + $markerPosition = strlen($text) - strlen($excerpt); + + $Excerpt = array('text' => $excerpt, 'context' => $text); + + foreach ($this->InlineTypes[$marker] as $inlineType) + { + # check to see if the current inline type is nestable in the current context + + if (isset($nonNestables[$inlineType])) + { + continue; + } + + $Inline = $this->{"inline$inlineType"}($Excerpt); + + if ( ! isset($Inline)) + { + continue; + } + + # makes sure that the inline belongs to "our" marker + + if (isset($Inline['position']) and $Inline['position'] > $markerPosition) + { + continue; + } + + # sets a default inline position + + if ( ! isset($Inline['position'])) + { + $Inline['position'] = $markerPosition; + } + + # cause the new element to 'inherit' our non nestables + + + $Inline['element']['nonNestables'] = isset($Inline['element']['nonNestables']) + ? array_merge($Inline['element']['nonNestables'], $nonNestables) + : $nonNestables + ; + + # the text that comes before the inline + $unmarkedText = substr($text, 0, $Inline['position']); + + # compile the unmarked text + $InlineText = $this->inlineText($unmarkedText); + $Elements[] = $InlineText['element']; + + # compile the inline + $Elements[] = $this->extractElement($Inline); + + # remove the examined text + $text = substr($text, $Inline['position'] + $Inline['extent']); + + continue 2; + } + + # the marker does not belong to an inline + + $unmarkedText = substr($text, 0, $markerPosition + 1); + + $InlineText = $this->inlineText($unmarkedText); + $Elements[] = $InlineText['element']; + + $text = substr($text, $markerPosition + 1); + } + + $InlineText = $this->inlineText($text); + $Elements[] = $InlineText['element']; + + foreach ($Elements as &$Element) + { + if ( ! isset($Element['autobreak'])) + { + $Element['autobreak'] = false; + } + } + + return $Elements; + } + + # + # ~ + # + + protected function inlineText($text) + { + $Inline = array( + 'extent' => strlen($text), + 'element' => array(), + ); + + $Inline['element']['elements'] = self::pregReplaceElements( + $this->breaksEnabled ? '/[ ]*+\n/' : '/(?:[ ]*+\\\\|[ ]{2,}+)\n/', + array( + array('name' => 'br'), + array('text' => "\n"), + ), + $text + ); + + return $Inline; + } + + protected function inlineCode($Excerpt) + { + $marker = $Excerpt['text'][0]; + + if (preg_match('/^(['.$marker.']++)[ ]*+(.+?)[ ]*+(?<!['.$marker.'])\1(?!'.$marker.')/s', $Excerpt['text'], $matches)) + { + $text = $matches[2]; + $text = preg_replace('/[ ]*+\n/', ' ', $text); + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'code', + 'text' => $text, + ), + ); + } + } + + protected function inlineEmailTag($Excerpt) + { + $hostnameLabel = '[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?'; + + $commonMarkEmail = '[a-zA-Z0-9.!#$%&\'*+\/=?^_`{|}~-]++@' + . $hostnameLabel . '(?:\.' . $hostnameLabel . ')*'; + + if (strpos($Excerpt['text'], '>') !== false + and preg_match("/^<((mailto:)?$commonMarkEmail)>/i", $Excerpt['text'], $matches) + ){ + $url = $matches[1]; + + if ( ! isset($matches[2])) + { + $url = "mailto:$url"; + } + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'a', + 'text' => $matches[1], + 'attributes' => array( + 'href' => $url, + ), + ), + ); + } + } + + protected function inlineEmphasis($Excerpt) + { + if ( ! isset($Excerpt['text'][1])) + { + return; + } + + $marker = $Excerpt['text'][0]; + + if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches)) + { + $emphasis = 'strong'; + } + elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches)) + { + $emphasis = 'em'; + } + else + { + return; + } + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => $emphasis, + 'handler' => array( + 'function' => 'lineElements', + 'argument' => $matches[1], + 'destination' => 'elements', + ) + ), + ); + } + + protected function inlineEscapeSequence($Excerpt) + { + if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters)) + { + return array( + 'element' => array('rawHtml' => $Excerpt['text'][1]), + 'extent' => 2, + ); + } + } + + protected function inlineImage($Excerpt) + { + if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[') + { + return; + } + + $Excerpt['text']= substr($Excerpt['text'], 1); + + $Link = $this->inlineLink($Excerpt); + + if ($Link === null) + { + return; + } + + $Inline = array( + 'extent' => $Link['extent'] + 1, + 'element' => array( + 'name' => 'img', + 'attributes' => array( + 'src' => $Link['element']['attributes']['href'], + 'alt' => $Link['element']['handler']['argument'], + ), + 'autobreak' => true, + ), + ); + + $Inline['element']['attributes'] += $Link['element']['attributes']; + + unset($Inline['element']['attributes']['href']); + + return $Inline; + } + + protected function inlineLink($Excerpt) + { + $Element = array( + 'name' => 'a', + 'handler' => array( + 'function' => 'lineElements', + 'argument' => null, + 'destination' => 'elements', + ), + 'nonNestables' => array('Url', 'Link'), + 'attributes' => array( + 'href' => null, + 'title' => null, + ), + ); + + $extent = 0; + + $remainder = $Excerpt['text']; + + if (preg_match('/\[((?:[^][]++|(?R))*+)\]/', $remainder, $matches)) + { + $Element['handler']['argument'] = $matches[1]; + + $extent += strlen($matches[0]); + + $remainder = substr($remainder, $extent); + } + else + { + return; + } + + if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*+"|\'[^\']*+\'))?\s*+[)]/', $remainder, $matches)) + { + $Element['attributes']['href'] = $matches[1]; + + if (isset($matches[2])) + { + $Element['attributes']['title'] = substr($matches[2], 1, - 1); + } + + $extent += strlen($matches[0]); + } + else + { + if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches)) + { + $definition = strlen($matches[1]) ? $matches[1] : $Element['handler']['argument']; + $definition = strtolower($definition); + + $extent += strlen($matches[0]); + } + else + { + $definition = strtolower($Element['handler']['argument']); + } + + if ( ! isset($this->DefinitionData['Reference'][$definition])) + { + return; + } + + $Definition = $this->DefinitionData['Reference'][$definition]; + + $Element['attributes']['href'] = $Definition['url']; + $Element['attributes']['title'] = $Definition['title']; + } + + return array( + 'extent' => $extent, + 'element' => $Element, + ); + } + + protected function inlineMarkup($Excerpt) + { + if ($this->markupEscaped or $this->safeMode or strpos($Excerpt['text'], '>') === false) + { + return; + } + + if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w[\w-]*+[ ]*+>/s', $Excerpt['text'], $matches)) + { + return array( + 'element' => array('rawHtml' => $matches[0]), + 'extent' => strlen($matches[0]), + ); + } + + if ($Excerpt['text'][1] === '!' and preg_match('/^<!---?[^>-](?:-?+[^-])*-->/s', $Excerpt['text'], $matches)) + { + return array( + 'element' => array('rawHtml' => $matches[0]), + 'extent' => strlen($matches[0]), + ); + } + + if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w[\w-]*+(?:[ ]*+'.$this->regexHtmlAttribute.')*+[ ]*+\/?>/s', $Excerpt['text'], $matches)) + { + return array( + 'element' => array('rawHtml' => $matches[0]), + 'extent' => strlen($matches[0]), + ); + } + } + + protected function inlineSpecialCharacter($Excerpt) + { + if (substr($Excerpt['text'], 1, 1) !== ' ' and strpos($Excerpt['text'], ';') !== false + and preg_match('/^&(#?+[0-9a-zA-Z]++);/', $Excerpt['text'], $matches) + ) { + return array( + 'element' => array('rawHtml' => '&' . $matches[1] . ';'), + 'extent' => strlen($matches[0]), + ); + } + + return; + } + + protected function inlineStrikethrough($Excerpt) + { + if ( ! isset($Excerpt['text'][1])) + { + return; + } + + if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches)) + { + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'del', + 'handler' => array( + 'function' => 'lineElements', + 'argument' => $matches[1], + 'destination' => 'elements', + ) + ), + ); + } + } + + protected function inlineUrl($Excerpt) + { + if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/') + { + return; + } + + if (strpos($Excerpt['context'], 'http') !== false + and preg_match('/\bhttps?+:[\/]{2}[^\s<]+\b\/*+/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE) + ) { + $url = $matches[0][0]; + + $Inline = array( + 'extent' => strlen($matches[0][0]), + 'position' => $matches[0][1], + 'element' => array( + 'name' => 'a', + 'text' => $url, + 'attributes' => array( + 'href' => $url, + ), + ), + ); + + return $Inline; + } + } + + protected function inlineUrlTag($Excerpt) + { + if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w++:\/{2}[^ >]++)>/i', $Excerpt['text'], $matches)) + { + $url = $matches[1]; + + return array( + 'extent' => strlen($matches[0]), + 'element' => array( + 'name' => 'a', + 'text' => $url, + 'attributes' => array( + 'href' => $url, + ), + ), + ); + } + } + + # ~ + + protected function unmarkedText($text) + { + $Inline = $this->inlineText($text); + return $this->element($Inline['element']); + } + + # + # Handlers + # + + protected function handle(array $Element) + { + if (isset($Element['handler'])) + { + if (!isset($Element['nonNestables'])) + { + $Element['nonNestables'] = array(); + } + + if (is_string($Element['handler'])) + { + $function = $Element['handler']; + $argument = $Element['text']; + unset($Element['text']); + $destination = 'rawHtml'; + } + else + { + $function = $Element['handler']['function']; + $argument = $Element['handler']['argument']; + $destination = $Element['handler']['destination']; + } + + $Element[$destination] = $this->{$function}($argument, $Element['nonNestables']); + + if ($destination === 'handler') + { + $Element = $this->handle($Element); + } + + unset($Element['handler']); + } + + return $Element; + } + + protected function handleElementRecursive(array $Element) + { + return $this->elementApplyRecursive(array($this, 'handle'), $Element); + } + + protected function handleElementsRecursive(array $Elements) + { + return $this->elementsApplyRecursive(array($this, 'handle'), $Elements); + } + + protected function elementApplyRecursive($closure, array $Element) + { + $Element = call_user_func($closure, $Element); + + if (isset($Element['elements'])) + { + $Element['elements'] = $this->elementsApplyRecursive($closure, $Element['elements']); + } + elseif (isset($Element['element'])) + { + $Element['element'] = $this->elementApplyRecursive($closure, $Element['element']); + } + + return $Element; + } + + protected function elementApplyRecursiveDepthFirst($closure, array $Element) + { + if (isset($Element['elements'])) + { + $Element['elements'] = $this->elementsApplyRecursiveDepthFirst($closure, $Element['elements']); + } + elseif (isset($Element['element'])) + { + $Element['element'] = $this->elementsApplyRecursiveDepthFirst($closure, $Element['element']); + } + + $Element = call_user_func($closure, $Element); + + return $Element; + } + + protected function elementsApplyRecursive($closure, array $Elements) + { + foreach ($Elements as &$Element) + { + $Element = $this->elementApplyRecursive($closure, $Element); + } + + return $Elements; + } + + protected function elementsApplyRecursiveDepthFirst($closure, array $Elements) + { + foreach ($Elements as &$Element) + { + $Element = $this->elementApplyRecursiveDepthFirst($closure, $Element); + } + + return $Elements; + } + + protected function element(array $Element) + { + if ($this->safeMode) + { + $Element = $this->sanitiseElement($Element); + } + + # identity map if element has no handler + $Element = $this->handle($Element); + + $hasName = isset($Element['name']); + + $markup = ''; + + if ($hasName) + { + $markup .= '<' . $Element['name']; + + if (isset($Element['attributes'])) + { + foreach ($Element['attributes'] as $name => $value) + { + if ($value === null) + { + continue; + } + + $markup .= " $name=\"".self::escape($value).'"'; + } + } + } + + $permitRawHtml = false; + + if (isset($Element['text'])) + { + $text = $Element['text']; + } + // very strongly consider an alternative if you're writing an + // extension + elseif (isset($Element['rawHtml'])) + { + $text = $Element['rawHtml']; + + $allowRawHtmlInSafeMode = isset($Element['allowRawHtmlInSafeMode']) && $Element['allowRawHtmlInSafeMode']; + $permitRawHtml = !$this->safeMode || $allowRawHtmlInSafeMode; + } + + $hasContent = isset($text) || isset($Element['element']) || isset($Element['elements']); + + if ($hasContent) + { + $markup .= $hasName ? '>' : ''; + + if (isset($Element['elements'])) + { + $markup .= $this->elements($Element['elements']); + } + elseif (isset($Element['element'])) + { + $markup .= $this->element($Element['element']); + } + else + { + if (!$permitRawHtml) + { + $markup .= self::escape($text, true); + } + else + { + $markup .= $text; + } + } + + $markup .= $hasName ? '</' . $Element['name'] . '>' : ''; + } + elseif ($hasName) + { + $markup .= ' />'; + } + + return $markup; + } + + protected function elements(array $Elements) + { + $markup = ''; + + $autoBreak = true; + + foreach ($Elements as $Element) + { + if (empty($Element)) + { + continue; + } + + $autoBreakNext = (isset($Element['autobreak']) + ? $Element['autobreak'] : isset($Element['name']) + ); + // (autobreak === false) covers both sides of an element + $autoBreak = !$autoBreak ? $autoBreak : $autoBreakNext; + + $markup .= ($autoBreak ? "\n" : '') . $this->element($Element); + $autoBreak = $autoBreakNext; + } + + $markup .= $autoBreak ? "\n" : ''; + + return $markup; + } + + # ~ + + protected function li($lines) + { + $Elements = $this->linesElements($lines); + + if ( ! in_array('', $lines) + and isset($Elements[0]) and isset($Elements[0]['name']) + and $Elements[0]['name'] === 'p' + ) { + unset($Elements[0]['name']); + } + + return $Elements; + } + + # + # AST Convenience + # + + /** + * Replace occurrences $regexp with $Elements in $text. Return an array of + * elements representing the replacement. + */ + protected static function pregReplaceElements($regexp, $Elements, $text) + { + $newElements = array(); + + while (preg_match($regexp, $text, $matches, PREG_OFFSET_CAPTURE)) + { + $offset = $matches[0][1]; + $before = substr($text, 0, $offset); + $after = substr($text, $offset + strlen($matches[0][0])); + + $newElements[] = array('text' => $before); + + foreach ($Elements as $Element) + { + $newElements[] = $Element; + } + + $text = $after; + } + + $newElements[] = array('text' => $text); + + return $newElements; + } + + # + # Deprecated Methods + # + + function parse($text) + { + $markup = $this->text($text); + + return $markup; + } + + protected function sanitiseElement(array $Element) + { + static $goodAttribute = '/^[a-zA-Z0-9][a-zA-Z0-9-_]*+$/'; + static $safeUrlNameToAtt = array( + 'a' => 'href', + 'img' => 'src', + ); + + if ( ! isset($Element['name'])) + { + unset($Element['attributes']); + return $Element; + } + + if (isset($safeUrlNameToAtt[$Element['name']])) + { + $Element = $this->filterUnsafeUrlInAttribute($Element, $safeUrlNameToAtt[$Element['name']]); + } + + if ( ! empty($Element['attributes'])) + { + foreach ($Element['attributes'] as $att => $val) + { + # filter out badly parsed attribute + if ( ! preg_match($goodAttribute, $att)) + { + unset($Element['attributes'][$att]); + } + # dump onevent attribute + elseif (self::striAtStart($att, 'on')) + { + unset($Element['attributes'][$att]); + } + } + } + + return $Element; + } + + protected function filterUnsafeUrlInAttribute(array $Element, $attribute) + { + foreach ($this->safeLinksWhitelist as $scheme) + { + if (self::striAtStart($Element['attributes'][$attribute], $scheme)) + { + return $Element; + } + } + + $Element['attributes'][$attribute] = str_replace(':', '%3A', $Element['attributes'][$attribute]); + + return $Element; + } + + # + # Static Methods + # + + protected static function escape($text, $allowQuotes = false) + { + return htmlspecialchars($text, $allowQuotes ? ENT_NOQUOTES : ENT_QUOTES, 'UTF-8'); + } + + protected static function striAtStart($string, $needle) + { + $len = strlen($needle); + + if ($len > strlen($string)) + { + return false; + } + else + { + return strtolower(substr($string, 0, $len)) === strtolower($needle); + } + } + + static function instance($name = 'default') + { + if (isset(self::$instances[$name])) + { + return self::$instances[$name]; + } + + $instance = new static(); + + self::$instances[$name] = $instance; + + return $instance; + } + + private static $instances = array(); + + # + # Fields + # + + protected $DefinitionData; + + # + # Read-Only + + protected $specialCharacters = array( + '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|', '~' + ); + + protected $StrongRegex = array( + '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*+[*])+?)[*]{2}(?![*])/s', + '_' => '/^__((?:\\\\_|[^_]|_[^_]*+_)+?)__(?!_)/us', + ); + + protected $EmRegex = array( + '*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s', + '_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us', + ); + + protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*+(?:\s*+=\s*+(?:[^"\'=<>`\s]+|"[^"]*+"|\'[^\']*+\'))?+'; + + protected $voidElements = array( + 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source', + ); + + protected $textLevelElements = array( + 'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont', + 'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing', + 'i', 'rp', 'del', 'code', 'strike', 'marquee', + 'q', 'rt', 'ins', 'font', 'strong', + 's', 'tt', 'kbd', 'mark', + 'u', 'xm', 'sub', 'nobr', + 'sup', 'ruby', + 'var', 'span', + 'wbr', 'time', + ); +}
\ No newline at end of file diff --git a/includes/util/agewarning.inc b/includes/util/agewarning.inc new file mode 100644 index 0000000..1f51647 --- /dev/null +++ b/includes/util/agewarning.inc @@ -0,0 +1,35 @@ +<?php + +function showWarning($name, $id, $system) { + $ntfy = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/app.json"), true)["ntfy"]; + + if (time() > 1677628800) { + file_get_contents('https://' . $ntfy["server"] . '/' . $ntfy['topic'], false, stream_context_create([ + 'http' => [ + 'method' => 'POST', + 'header' => + "Content-Type: text/plain\r\n" . + "Title: ⚠️ $name does not have an age or birth year set\r\n" . + "Priority: max\r\n" . + "Tags: switch\r\n" . + "Actions: view, Edit on Cold Haze, https://ponies.equestria.horse/-/metadata/" . ($system === "gdapd" ? "raindrops" : "cloudburst") . "/" . $id . "/, clear=true\r\n" . + "Authorization: Basic " . base64_encode($ntfy["user"] . ":" . $ntfy["password"]), + 'content' => "To make sure they appear on the fronting schedule (and to make sure they can front again), they need to set an age or birth year now." + ] + ])); + } else { + file_get_contents('https://' . $ntfy["server"] . '/' . $ntfy['topic'], false, stream_context_create([ + 'http' => [ + 'method' => 'POST', + 'header' => + "Content-Type: text/plain\r\n" . + "Title: ⚠️ $name does not have an age or birth year set\r\n" . + "Priority: max\r\n" . + "Tags: switch\r\n" . + "Actions: view, Edit on Cold Haze, https://ponies.equestria.horse/-/metadata/" . ($system === "gdapd" ? "raindrops" : "cloudburst") . "/" . $id . "/, clear=true\r\n" . + "Authorization: Basic " . base64_encode($ntfy["user"] . ":" . $ntfy["password"]), + 'content' => "To make sure they still appear on the fronting schedule after March 1st, they need to set an age or birth year now." + ] + ])); + } +}
\ No newline at end of file diff --git a/includes/util/banner.inc b/includes/util/banner.inc new file mode 100644 index 0000000..46d9ce5 --- /dev/null +++ b/includes/util/banner.inc @@ -0,0 +1,480 @@ +<?php + +require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/session.inc"; global $isLoggedIn; global $lang; global $pages; +require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/pronouns.inc"; +require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/functions.inc"; + +$travelling = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/travelling/travelling.json"), true); + +function _header_getMember(string $id, $system) { + $members = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$system/members.json"), true); + $member = null; + + foreach ($members as $m) { + if ($m["id"] === $id) $member = $m; + } + + return $member; +} + +function getMemberBannerData(string $id, string $system, bool $french = false) { + global $travelling; + global $isLoggedIn; + global $lang; + + $french = $lang["_french"]; + + $member = getSystemMember($system, $id); + $metadata = file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/metadata/$id.json") ? parseMetadata(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/metadata/$id.json"), true)) : parseMetadata([ + "bitset" => 4196352, + "regression" => null, + "median" => null, + "marefriends" => [], + "sexfriends" => [], + "sisters" => [], + "caretakers" => [] + ]); + + $prefixes = []; + foreach ($member['proxy_tags'] as $proxy) { + if ($travelling[$member['id']]["travelling"]) { + if (isset($travelling[$member['id']]['equestria']) && $travelling[$member['id']]['equestria']) { + $prefixes[] = null; + } else { + $prefixes[] = "+" . $proxy["prefix"] . ($proxy["suffix"] !== "" && $proxy["suffix"] !== null ? "..." . $proxy["suffix"] : ""); + } + } else { + $prefixes[] = $proxy["prefix"] . ($proxy["suffix"] !== "" && $proxy["suffix"] !== null ? "..." . $proxy["suffix"] : ""); + } + } + + $lastFronted = null; + if (!$metadata["median"]) { + $fronters = array_map(function ($item) { + return $item["id"]; + }, json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$system/fronters.json"), true)["members"]); + + if (in_array($id, $fronters)) { + $lastFronted = [ + 'now' => true, + 'relative' => timeAgo(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$system/fronters.json"), true)["timestamp"]), + 'absolute' => date("D j M Y, G:i:s (e)", strtotime(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$system/fronters.json"), true)["timestamp"])), + 'timestamp' => strtotime(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$system/fronters.json"), true)["timestamp"]), + 'duration' => [ + 'seconds' => null, + 'pretty' => null + ] + ]; + } else { + $switches = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$system/switches.json"), true); + + $thisMember = array_filter($switches, function ($item) { + global $memberData; + return in_array($memberData["id"], $item["members"]); + }); + + $thisMember = array_values($thisMember); + $frontingEnd = null; + + if (count($thisMember) > 0) { + $thisIndex = array_search($thisMember[0], $switches); + + $frontingStart = $thisMember[0]; + $frontingEnd = $switches[$thisIndex - 1]; + } + + if ($frontingEnd !== null && isset($frontingStart)) { + $seconds = (strtotime($frontingEnd["timestamp"]) - strtotime($frontingStart["timestamp"])); + + $lastFronted = [ + 'now' => false, + 'relative' => timeAgo($frontingEnd["timestamp"]), + 'absolute' => date("D j M Y, G:i:s (e)", strtotime($frontingEnd["timestamp"])), + 'timestamp' => strtotime($frontingEnd["timestamp"]), + 'duration' => [ + 'seconds' => $seconds, + 'pretty' => $seconds . ($french ? " secondes" : " seconds") + ] + ]; + if ($french) { + if ($seconds > 60) { + if ($seconds > 3600) { + $lastFronted['duration']['pretty'] = round($seconds / 3600) . " heures"; + } else { + $lastFronted['duration']['pretty'] = round($seconds / 60) . " minutes"; + } + } + } else { + if ($seconds > 60) { + if ($seconds > 3600) { + $lastFronted['duration']['pretty'] = round($seconds / 3600) . " hours"; + } else { + $lastFronted['duration']['pretty'] = round($seconds / 60) . " minutes"; + } + } + } + } + } + } + + $speciesList = []; + foreach ($metadata["species"] ?? [] as $species) { + if ($french) { + $name = match ($species) { + "earth" => $metadata["robot"] ? "Robot poney terrestre" : (!$metadata["plush"] ? "Poney terrestre" : "Poney terrestre en peluche"), + "alicorn" => $metadata["robot"] ? "Robot alicorne" : (!$metadata["plush"] ? "Alicorne" : "Alicorne en peluche"), + "crystal" => $metadata["robot"] ? "Robot poney de crystal" : (!$metadata["plush"] ? "Poney de crystal" : "Poney de crystal en peluche"), + "pegasus" => $metadata["robot"] ? "Robot pégase" : (!$metadata["plush"] ? "Pégase" : "Pégase en peluche"), + "batpony" => $metadata["robot"] ? "Robot Poney chauve souris" : (!$metadata["plush"] ? "Poney chauve souris" : "Poney chauve souris en peluche"), + "unicorn" => $metadata["robot"] ? "Robot licorne" : (!$metadata["plush"] ? "Licorne" : "Licorne en peluche"), + "changeling" => "Changelin", + default => $species . "_" . $metadata["robot"] + }; + } else { + $name = match ($species) { + "earth" => $metadata["robot"] ? "Robot earth pony" : (!$metadata["plush"] ? "Earth pony" : "Earth pony plush"), + "alicorn" => $metadata["robot"] ? "Robot alicorn" : (!$metadata["plush"] ? "Alicorn" : "Alicorn plush"), + "crystal" => $metadata["robot"] ? "Robot crystal pony" : (!$metadata["plush"] ? "Crystal pony" : "Crystal pony plush"), + "pegasus" => $metadata["robot"] ? "Robot pegasus" : (!$metadata["plush"] ? "Pegasus" : "Pegasus plush"), + "batpony" => $metadata["robot"] ? "Robot bat pony" : (!$metadata["plush"] ? "Bat pony" : "Bat pony plush"), + "unicorn" => $metadata["robot"] ? "Robot unicorn" : (!$metadata["plush"] ? "Unicorn" : "Unicorn plush"), + "changeling" => "Changeling", + default => $species . "_" . $metadata["robot"] + }; + } + + $speciesList[] = [ + "id" => $species, + "name" => $name, + "robot" => $metadata["robot"], + "plush" => $metadata["plush"], + "icon" => $species . ($metadata["robot"] ? "-robot" : ($metadata["plush"] ? "-plush" : "")) . ".png" + ]; + } + + $app = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/app.json"), true); + + $systemData = []; + $systemData['page'] = "/" . ($system === "gdapd" ? "raindrops" : ($system === $app["other"]["id"] ? $app["other"]["slug"] : "cloudburst")); + $systemData['icon'] = getAsset($system); + $systemData['name'] = $system === "gdapd" ? "Raindrops" : ($system === $app["other"]["id"] ? $app["other"]["short"] : "Cloudburst"); + $systemData['full_name'] = $system === "gdapd" ? "Raindrops System" : ($system === $app["other"]["id"] ? $app["other"]["name"] : "Cloudburst System"); + $systemData['temporary'] = false; + + if ($travelling[$member['id']]["travelling"] && !$travelling[$member['id']]["equestria"]) { + $systemData['page'] = "/" . ($system === "gdapd" ? "cloudburst" : "raindrops"); + $systemData['icon'] = getAsset($system === "gdapd" ? "ynmuc" : "gdapd"); + $systemData['name'] = $system === "gdapd" ? "Cloudburst" : "Raindrops"; + $systemData['full_name'] = $system === "gdapd" ? "Cloudburst System" : "Raindrops System"; + $systemData['temporary'] = true; + } + + if ($travelling[$member['id']]["travelling"] && $travelling[$member['id']]["equestria"]) { + $systemData['page'] = null; + $systemData['icon'] = "../logo/equestria.png"; + $systemData['name'] = "Equestria"; + $systemData['full_name'] = "Equestria"; + $systemData['temporary'] = true; + } + + $marefriends = []; + foreach ($metadata["marefriends"] as $marefriend) { + $mfSystem = explode("/", $marefriend)[0]; + $mfMemberID = explode("/", $marefriend)[1]; + $mfMember = array_filter(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$mfSystem/members.json"), true), function ($item) use ($mfMemberID) { + return $item["id"] === $mfMemberID; + }); + sort($mfMember); + $mfMember = $mfMember[0]; + + $marefriends[] = [ + "id" => $marefriend, + "link" => "/" . ($mfMember["name"]), + "icon" => getAsset($mfSystem, $mfMemberID, "heads"), + "name" => $mfMember["display_name"] ?? $mfMember["name"], + "full_name" => $mfMember["display_name"] ?? $mfMember["name"] + ]; + } + + $sexfriends = null; + + if ($isLoggedIn) { + $sexfriends = []; + foreach ($metadata["sexfriends"] as $marefriend) { + $mfSystem = explode("/", $marefriend)[0]; + $mfMemberID = explode("/", $marefriend)[1]; + $mfMember = array_filter(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$mfSystem/members.json"), true), function ($item) use ($mfMemberID) { + return $item["id"] === $mfMemberID; + }); + sort($mfMember); + $mfMember = $mfMember[0]; + + $sexfriends[] = [ + "id" => $marefriend, + "link" => "/" . ($mfMember["name"]), + "icon" => getAsset($mfSystem, $mfMemberID, "heads"), + "name" => $mfMember["display_name"] ?? $mfMember["name"], + "full_name" => $mfMember["display_name"] ?? $mfMember["name"] + ]; + } + } + + $sisters = []; + foreach ($metadata["sisters"] as $marefriend) { + $mfSystem = explode("/", $marefriend)[0]; + $mfMemberID = explode("/", $marefriend)[1]; + $mfMember = array_filter(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$mfSystem/members.json"), true), function ($item) use ($mfMemberID) { + return $item["id"] === $mfMemberID; + }); + sort($mfMember); + $mfMember = $mfMember[0]; + + $sisters[] = [ + "id" => $marefriend, + "link" => "/" . ($mfMember["name"]), + "icon" => getAsset($mfSystem, $mfMemberID, "heads"), + "name" => $mfMember["display_name"] ?? $mfMember["name"], + "full_name" => $mfMember["display_name"] ?? $mfMember["name"] + ]; + } + + $caretakers = null; + + if ($metadata["little"] >= 2) { + $caretakers = []; + foreach ($metadata["caretakers"] as $marefriend) { + $mfSystem = explode("/", $marefriend)[0]; + $mfMemberID = explode("/", $marefriend)[1]; + $mfMember = array_filter(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$mfSystem/members.json"), true), function ($item) use ($mfMemberID) { + return $item["id"] === $mfMemberID; + }); + sort($mfMember); + $mfMember = $mfMember[0]; + + $caretakers[] = [ + "id" => $marefriend, + "link" => "/" . ($mfMember["name"]), + "icon" => getAsset($mfSystem, $mfMemberID, "heads"), + "name" => $mfMember["display_name"] ?? $mfMember["name"], + "full_name" => $mfMember["display_name"] ?? $mfMember["name"] + ]; + } + } + + $badges = []; + + if ($metadata["host"] ?? false) { + if (!$travelling[$member['id']]["travelling"]) { + $badges[] = [ + "id" => "mcf", + "color" => "primary", + "html" => ( + $french + ? + '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Fronteuse la plus présente</b><br>Elle est le membre qui fronte le plus dans son système." class="badge rounded-pill bg-primary">Fronteuse la plus présente</span>' + : + '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Most common fronter</b><br>' . ucfirst(getMemberPronouns($member['pronouns'])["subjective"]) . ' ' . (getMemberPronouns($member['pronouns'])["third"] ? "is" : "are") . ' the one who fronts the most often in ' . getMemberPronouns($member['pronouns'])["possessive_det"] . ' system." class="badge rounded-pill bg-primary">Most common fronter</span>' + ) + ]; + } + } + + /*if (($metadata["age_spells"] ?? false) && !$french) { + $badges[] = [ + "id" => "age_spells", + "color" => "#6f42c1", + "html" => '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Affected by age spells</b><br>' . ucfirst(getMemberPronouns($member['pronouns'])["subjective"]) . ' can feel younger than ' . getMemberPronouns($member['pronouns'])["subjective"] . ' actually ' . (getMemberPronouns($member['pronouns'])["third"] ? "is" : "are") . ' when somepony else casts an age spell on ' . getMemberPronouns($member['pronouns'])["object"] . '." class="badge rounded-pill" style="background-color: #6f42c1;">Affected by age spells</span>' + ]; + }*/ + + if ($metadata["fictive"] ?? false) { + $badges[] = [ + "id" => "fictive", + "color" => "info", + "html" => ( + $french + ? + '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Fictive</b><br>Elle est basée sur la personnalité, apparence et comportement d\'un personnage qui est fictif dans ce monde." class="badge rounded-pill bg-info">Fictive</span>' + : + '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Fictive</b><br>' . ucfirst(getMemberPronouns($member['pronouns'])["subjective"]) . ' ' . (getMemberPronouns($member['pronouns'])["third"] ? "is" : "are") . ' based on the personality, look and behavior of a character that is fictional in this world." class="badge rounded-pill bg-info">Fictive</span>' + ) + ]; + } + + if ($metadata["persecutor"] ?? false) { + $badges[] = [ + "id" => "persecutor", + "color" => "danger", + "html" => ( + $french + ? + '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Persécutrice</b><br>Elle se comporte de façon nuisible à l\'égart des autres ou des autres membres du système, potentiellement comme une tentative malavisée de protéger le système." class="badge rounded-pill bg-danger">Persécutrice</span>' + : + '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Persecutor</b><br>' . ucfirst(getMemberPronouns($member['pronouns'])["subjective"]) . ' ' . (getMemberPronouns($member['pronouns'])["third"] ? "acts" : "act") . ' harmfully towards other system members and/or others, potentially as a misguided attempt to protect the system." class="badge rounded-pill bg-danger">Persecutor</span>' + ) + ]; + } + + if ($metadata["less_frequent"] ?? false) { + $badges[] = [ + "id" => "nonverbal", + "color" => "#6610f2", + "html" => ( + $french + ? + '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Fronts less often</b><br>' . ucfirst(getMemberPronouns($member['pronouns'])["subjective"]) . ' ' . (getMemberPronouns($member['pronouns'])["third"] ? "front" : "fronts") . ' less often than ' . getMemberPronouns($member['pronouns'])["possessive_det"] . ' headmates, this can due to various reasons." class="badge rounded-pill" style="background-color:#6610f2;">Fronte moins souvent</span>' + : + '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Fronts less often</b><br>' . ucfirst(getMemberPronouns($member['pronouns'])["subjective"]) . ' ' . (getMemberPronouns($member['pronouns'])["third"] ? "front" : "fronts") . ' less often than ' . getMemberPronouns($member['pronouns'])["possessive_det"] . ' headmates, this can due to various reasons." class="badge rounded-pill" style="background-color:#6610f2;">Fronts less often</span>' + ) + ]; + } + + if ($metadata["nonverbal"] ?? false) { + $badges[] = [ + "id" => "nonverbal", + "color" => "#20c997", + "html" => ( + $french + ? + '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Non verbale dans la réalité</b><br>' . ucfirst(getMemberPronouns($member['pronouns'])["subjective"]) . ' ' . (getMemberPronouns($member['pronouns'])["third"] ? "is" : "are") . ' non verbal in real life, although text communication is still possible." class="badge rounded-pill" style="background-color:#20c997;">Non verbal IRL</span>' + : + '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Non verbal IRL</b><br>' . ucfirst(getMemberPronouns($member['pronouns'])["subjective"]) . ' ' . (getMemberPronouns($member['pronouns'])["third"] ? "is" : "are") . ' non verbal in real life, although text communication is still possible." class="badge rounded-pill" style="background-color:#20c997;">Non verbal IRL</span>' + ) + ]; + } + + if ($member["name"] === "fusion") { + $badges[] = [ + "id" => "fusion", + "color" => "d63384", + "html" => '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Fusion</b><br>She is the result of multiple ponies purposefully merging in a way that makes them impossible to tell apart." class="badge rounded-pill" style="background-color:#d63384;">Fusion</span>' + ]; + } + + if (($metadata["leader"] ?? false)) { + $badges[] = [ + "id" => "leader", + "color" => "d6a833", + "html" => ( + $french + ? + '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Leader</b><br>' . ucfirst(getMemberPronouns($member['pronouns'])["subjective"]) . ' ' . (getMemberPronouns($member['pronouns'])["third"] ? "is" : "are") . ' a leader in ' . getMemberPronouns($member['pronouns'])["possessive_det"] . ' system." class="badge rounded-pill" style="background-color:#fd7e14;">Dirigeante</span>' + : + '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Leader</b><br>' . ucfirst(getMemberPronouns($member['pronouns'])["subjective"]) . ' ' . (getMemberPronouns($member['pronouns'])["third"] ? "is" : "are") . ' a leader in ' . getMemberPronouns($member['pronouns'])["possessive_det"] . ' system." class="badge rounded-pill" style="background-color:#fd7e14;">Leader</span>' + ) + ]; + } + + if ($metadata["protector"] ?? false) { + $badges[] = [ + "id" => "protector", + "color" => "black", + "html" => ( + $french + ? + '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>' . (getMemberPronouns($member['pronouns'])["gender"] === "female" ? "Protectrice" : (getMemberPronouns($member['pronouns'])["gender"] === "male" ? "Protecteur" : "Protectrice")) . '</b><br>' . ucfirst(getMemberPronouns($member['pronouns'])["subjective"]) . ' ' . (getMemberPronouns($member['pronouns'])["third"] ? "is" : "are") . ' a protector in the system and will front when somepony cannot handle it anymore." class="badge rounded-pill bg-black">' . (getMemberPronouns($member['pronouns'])["gender"] === "female" ? "Protectrice" : (getMemberPronouns($member['pronouns'])["gender"] === "male" ? "Protecteur" : "Protectrice")) . '</span>' + : + '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Protector</b><br>' . ucfirst(getMemberPronouns($member['pronouns'])["subjective"]) . ' ' . (getMemberPronouns($member['pronouns'])["third"] ? "is" : "are") . ' a protector in the system and will front when somepony cannot handle it anymore." class="badge rounded-pill bg-black">Protector</span>' + ) + ]; + } + + if (($metadata["little"] ?? 0) === 2) { + $badges[] = [ + "id" => "little", + "color" => "success", + "html" => ( + $french + ? + '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Petit' . (getMemberPronouns($member['pronouns'])["gender"] === "female" ? "e" : (getMemberPronouns($member['pronouns'])["gender"] === "male" ? "" : "·e")) . '</b><br>' . ucfirst(getMemberPronouns($member['pronouns'])["subjective"]) . ' ' . (getMemberPronouns($member['pronouns'])["third"] ? "is" : "are") . ' mentally younger, and therefore behaves and feels younger than the body is." class="badge rounded-pill bg-success">Petit' . (getMemberPronouns($member['pronouns'])["gender"] === "female" ? "e" : (getMemberPronouns($member['pronouns'])["gender"] === "male" ? "" : "·e")) . '</span>' + : + '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Little</b><br>' . ucfirst(getMemberPronouns($member['pronouns'])["subjective"]) . ' ' . (getMemberPronouns($member['pronouns'])["third"] ? "is" : "are") . ' mentally younger, and therefore behaves and feels younger than the body is." class="badge rounded-pill bg-success">Little</span>' + ) + ]; + } + + if (($metadata["little"] ?? 0) === 3) { + $badges[] = [ + "id" => "younger", + "color" => "dark", + "html" => ( + $french + ? + '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Plus jeune</b><br>' . ucfirst(getMemberPronouns($member['pronouns'])["subjective"]) . ' ' . (getMemberPronouns($member['pronouns'])["third"] ? "is" : "are") . ' younger than the body, but not young enough to be classified as a little." class="badge rounded-pill bg-dark">Plus jeune</span>' + : + '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Younger</b><br>' . ucfirst(getMemberPronouns($member['pronouns'])["subjective"]) . ' ' . (getMemberPronouns($member['pronouns'])["third"] ? "is" : "are") . ' younger than the body, but not young enough to be classified as a little." class="badge rounded-pill bg-dark">Younger</span>' + ) + ]; + } + + if (($metadata["age_regressor"] ?? false)) { + if ($metadata["regression"] !== null && $metadata["regression"] !== false) { + $regression = _header_getMember($metadata["regression"], $system); + $badges[] = [ + "id" => "regressor_median", + "color" => "secondary", + "html" => ( + $french + ? + '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Régresseur d\'âge</b><br>' . ucfirst(getMemberPronouns($member['pronouns'])["subjective"]) . ' ' . (getMemberPronouns($member['pronouns'])["third"] ? "is" : "are") . ' capable of regressing ' . getMemberPronouns($member['pronouns'])["possessive_det"] . ' mental age, which causes ' . getMemberPronouns($member['pronouns'])["object"] . ' to become ' . (getMiniName($regression["display_name"] ?? $regression["name"])) . ', temporarily behaving and feeling younger than the body is." class="badge rounded-pill bg-secondary">Régresse en âge en <a href="/' . $regression["name"] . '">'. (getMiniName($regression["display_name"] ?? $regression["name"])) . '</a></span>' + : + '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Age regressor</b><br>' . ucfirst(getMemberPronouns($member['pronouns'])["subjective"]) . ' ' . (getMemberPronouns($member['pronouns'])["third"] ? "is" : "are") . ' capable of regressing ' . getMemberPronouns($member['pronouns'])["possessive_det"] . ' mental age, which causes ' . getMemberPronouns($member['pronouns'])["object"] . ' to become ' . (getMiniName($regression["display_name"] ?? $regression["name"])) . ', temporarily behaving and feeling younger than the body is." class="badge rounded-pill bg-secondary">Age regresses into <a href="/' . $regression["name"] . '">'. (getMiniName($regression["display_name"] ?? $regression["name"])) . '</a></span>' + ) + ]; + } + } + + if ($metadata["median"] !== null && $metadata["median"] !== false) { + $source = _header_getMember($metadata["median"], $system); + if ($metadata["little"] > 0) { + $badges[] = [ + "id" => "regressed", + "color" => "warning", + "html" => ( + $french + ? + '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Régressée en âge</b><br>' . ucfirst(getMemberPronouns($member['pronouns'])["subjective"]) . ' has regressed ' . getMemberPronouns($member['pronouns'])["possessive_det"] . ' mental age, making ' . getMemberPronouns($member['pronouns'])["object"] . ' ' . getMiniName($member["display_name"] ?? $member["name"]) . ' instead of ' . (getMiniName($source["display_name"] ?? $source["name"])) . ', temporarily behaving and feeling younger than the body is." class="badge rounded-pill bg-warning">Régressé en âge de <a href="/' . $source["name"] . '">' . (getMiniName($source["display_name"] ?? $source["name"])) . '</a></span>' + : + '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Age regressed</b><br>' . ucfirst(getMemberPronouns($member['pronouns'])["subjective"]) . ' has regressed ' . getMemberPronouns($member['pronouns'])["possessive_det"] . ' mental age, making ' . getMemberPronouns($member['pronouns'])["object"] . ' ' . getMiniName($member["display_name"] ?? $member["name"]) . ' instead of ' . (getMiniName($source["display_name"] ?? $source["name"])) . ', temporarily behaving and feeling younger than the body is." class="badge rounded-pill bg-warning">Age regressed from <a href="/' . $source["name"] . '">' . (getMiniName($source["display_name"] ?? $source["name"])) . '</a></span>' + ) + ]; + } else { + $badges[] = [ + "id" => "facet", + "color" => "light", + "html" => ( + $french + ? + '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Facette</b><br>' . ucfirst(getMemberPronouns($member['pronouns'])["subjective"]) . ' is a facet of ' . getMiniName($source["display_name"] ?? $source["name"]) . ', meaning ' . getMemberPronouns($member['pronouns'])["subjective"] . ' ' . (getMemberPronouns($member['pronouns'])["third"] ? "is" : "are") . ' not totally independent of ' . getMiniName($source["display_name"] ?? $source["name"]) . '." class="badge rounded-pill bg-light">Facette de <a href="/' . $source["name"] . '">' . getMiniName($source["display_name"] ?? $source["name"]) . '</a></span>' + : + '<span data-bs-toggle="tooltip" data-bs-html="true" title="<b>Facet</b><br>' . ucfirst(getMemberPronouns($member['pronouns'])["subjective"]) . ' is a facet of ' . getMiniName($source["display_name"] ?? $source["name"]) . ', meaning ' . getMemberPronouns($member['pronouns'])["subjective"] . ' ' . (getMemberPronouns($member['pronouns'])["third"] ? "is" : "are") . ' not totally independent of ' . getMiniName($source["display_name"] ?? $source["name"]) . '." class="badge rounded-pill bg-light">Facet of <a href="/' . $source["name"] . '">' . getMiniName($source["display_name"] ?? $source["name"]) . '</a></span>' + ) + ]; + } + } + + return [ + 'id' => $member['name'], + 'color' => $member["color"] ?? "000000", + 'icon' => [ + "online" => getAsset($system, $member["id"]), + "offline" => "pf-$system-$id.webp" + ], + 'median' => $metadata["median"], + 'little' => $metadata["little"] >= 2, + 'name' => $member["display_name"] ?? $member["name"], + 'badges' => $badges, + 'prefixes' => $prefixes, + 'pronouns' => getTooltipsFromMark($member["pronouns"], $french) ?? "<span data-bs-toggle='tooltip' title='Pronouns not specified' class='text-muted'>they/them</span>", + 'pronouns_usage' => getMemberPronouns($member['pronouns']), + 'last_fronted' => $lastFronted, + 'species' => $speciesList, + 'system' => $systemData, + 'relations' => [ + 'marefriends' => $marefriends ?? [], + 'sexfriends' => $sexfriends ?? null, + 'sisters' => $sisters ?? [], + 'caretakers' => $caretakers ?? [] + ] + ]; +}
\ No newline at end of file diff --git a/includes/util/bitset.inc b/includes/util/bitset.inc new file mode 100644 index 0000000..131cb39 --- /dev/null +++ b/includes/util/bitset.inc @@ -0,0 +1,21 @@ +<?php + +function parseMetadata ($metadata) { + $metadata["little"] = 0; + + $age = -1; + + if (isset($metadata["birth"]["age"]) && $metadata["birth"]["age"] !== 0) { + $age = $metadata["birth"]["age"]; + } else if (isset($metadata["birth"]["year"]) && $metadata["birth"]["year"] > 1990) { + $age = (int)date('Y') - $metadata["birth"]["year"] + (strtotime(date('Y') . "-" . $metadata["birth"]["date"]) <= time() ? 0 : -1); + } + + if ($age > 0 && $age <= 10) { + $metadata["little"] = 2; + } else if ($age > 0 && $age <= 15) { + $metadata["little"] = 3; + } + + return $metadata; +}
\ No newline at end of file diff --git a/includes/util/functions.inc b/includes/util/functions.inc new file mode 100644 index 0000000..24b059a --- /dev/null +++ b/includes/util/functions.inc @@ -0,0 +1,551 @@ +<?php + +require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/score.inc"; +require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/bitset.inc"; +require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/homepage.inc"; + +if (!function_exists("peh_error")) { + function peh_error($message, $code = 500): void { + header("Location: /?em=" . urlencode(base64_encode($message)) . "&ec=" . $code); + die(); + } +} + +if (!function_exists("getAsset")) { + function getAsset($systemID, $memberID = null, $type = "avatars") { + $app = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/app.json"), true); + $systemFile = (isset($app["other"]) && $systemID === $app["other"]["id"]) ? "other" : $systemID; + + if (file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemFile/general.json")) { + $id1 = preg_replace("/^([\da-f]{8})-([\da-f]{4})-([\da-f]{4})-([\da-f]{4})-([\da-f]{12})$/", "$1$2$3$4$5", json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemFile/general.json"), true)["uuid"]); + + if (isset($memberID)) { + $members = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemFile/members.json"), true); + $list = array_map(function ($i) { + return $i["id"]; + }, $members); + + if (in_array($memberID, $list)) { + $id2 = preg_replace("/^([\da-f]{8})-([\da-f]{4})-([\da-f]{4})-([\da-f]{4})-([\da-f]{12})$/", "$1$2$3$4$5", (getMemberWithoutSystem($memberID) ?? ['uuid' => ''])["uuid"]); + } else { + return "/error/nomember/?s=$systemID&m=$memberID&t=$type"; + } + + $id = $id1 . $id2; + + if (str_ends_with((getMemberWithoutSystem($memberID) ?? ['name' => ''])["name"], "-travelling")) { + $id1 = preg_replace("/^([\da-f]{8})-([\da-f]{4})-([\da-f]{4})-([\da-f]{4})-([\da-f]{12})$/", "$1$2$3$4$5", json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/" . ($systemID === "gdapd" ? "ynmuc" : "gdapd") . "/general.json"), true)["uuid"]); + + $members = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/" . ($systemID === "gdapd" ? "ynmuc" : "gdapd") . "/members.json"), true); + $list = array_map(function ($i) { + return $i["name"]; + }, $members); + + if (in_array(substr(getMemberWithoutSystem($memberID)["name"], 0, -11), $list)) { + $id2 = preg_replace("/^([\da-f]{8})-([\da-f]{4})-([\da-f]{4})-([\da-f]{4})-([\da-f]{12})$/", "$1$2$3$4$5", getMemberFromName(substr(getMemberWithoutSystem($memberID)["name"], 0, -11))["uuid"]); + } else { + return "/error/nomember/?s=$systemID&m=$memberID&t=$type"; + } + + $id = $id1 . $id2; + } + } else { + $id = $id1; + } + + if ($type === "bodies" || $type === "heads") { + if (file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/" . $type . "/" . $id . ".png")) { + return "/assets/" . $type . "/" . $id . ".png"; + } else { + return "/error/nofile/?s=$systemID&m=$memberID&t=$type"; + } + } else { + if (file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/" . $type . "/" . $id . ".webp")) { + return "/assets/" . $type . "/" . $id . ".webp"; + } else { + return "/error/nofile/?s=$systemID&m=$memberID&t=$type"; + } + } + } else { + return "/error/nosys/?s=$systemID&m=$memberID&t=$type"; + } + } +} + +if (!function_exists("rgbToHsl")) { + function rgbToHsl($r, $g, $b) { + $oldR = $r; + $oldG = $g; + $oldB = $b; + + $r /= 255; + $g /= 255; + $b /= 255; + + $max = max($r, $g, $b); + $min = min($r, $g, $b); + + $l = ($max + $min) / 2; + $d = $max - $min; + + if ($d == 0) { + $h = $s = 0; + } else { + $s = $d / (1 - abs(2 * $l - 1)); + + switch ($max) { + case $r: + $h = 60 * fmod((($g - $b) / $d), 6); + if ($b > $g) { + $h += 360; + } + break; + + case $g: + $h = 60 * (($b - $r) / $d + 2); + break; + + case $b: + $h = 60 * (($r - $g) / $d + 4); + break; + } + } + + return array(round($h, 2), round($s, 2), round($l, 2)); + } +} + +if (!function_exists("imageCreateCorners")) { + function imageCreateCorners($sourceImageFile, $radius) { + # test source image + if (file_exists($sourceImageFile)) { + $res = is_array($info = getimagesize($sourceImageFile)); + } + else $res = false; + + # open image + if ($res) { + $w = $info[0]; + $h = $info[1]; + switch ($info['mime']) { + case 'image/jpeg': $src = imagecreatefromjpeg($sourceImageFile); + break; + case 'image/gif': $src = imagecreatefromgif($sourceImageFile); + break; + case 'image/png': $src = imagecreatefrompng($sourceImageFile); + break; + default: + $res = false; + } + } + + # create corners + if ($res) { + + $q = 10; # change this if you want + $radius *= $q; + + # find unique color + do { + $r = rand(0, 255); + $g = rand(0, 255); + $b = rand(0, 255); + } + while (imagecolorexact($src, $r, $g, $b) < 0); + + $nw = $w*$q; + $nh = $h*$q; + + $img = imagecreatetruecolor($nw, $nh); + $alphacolor = imagecolorallocatealpha($img, $r, $g, $b, 127); + imagealphablending($img, false); + imagesavealpha($img, true); + imagefilledrectangle($img, 0, 0, $nw, $nh, $alphacolor); + + imagefill($img, 0, 0, $alphacolor); + imagecopyresampled($img, $src, 0, 0, 0, 0, $nw, $nh, $w, $h); + + imagearc($img, $radius-1, $radius-1, $radius*2, $radius*2, 180, 270, $alphacolor); + imagefilltoborder($img, 0, 0, $alphacolor, $alphacolor); + imagearc($img, $nw-$radius, $radius-1, $radius*2, $radius*2, 270, 0, $alphacolor); + imagefilltoborder($img, $nw-1, 0, $alphacolor, $alphacolor); + imagearc($img, $radius-1, $nh-$radius, $radius*2, $radius*2, 90, 180, $alphacolor); + imagefilltoborder($img, 0, $nh-1, $alphacolor, $alphacolor); + imagearc($img, $nw-$radius, $nh-$radius, $radius*2, $radius*2, 0, 90, $alphacolor); + imagefilltoborder($img, $nw-1, $nh-1, $alphacolor, $alphacolor); + imagealphablending($img, true); + imagecolortransparent($img, $alphacolor); + + # resize image down + $dest = imagecreatetruecolor($w, $h); + imagealphablending($dest, false); + imagesavealpha($dest, true); + imagefilledrectangle($dest, 0, 0, $w, $h, $alphacolor); + imagecopyresampled($dest, $img, 0, 0, 0, 0, $w, $h, $nw, $nh); + + # output image + $res = $dest; + imagedestroy($src); + imagedestroy($img); + } + + return $res; + } +} + +if (!function_exists("getMiniName")) { + function getMiniName(string $name) { + $parts = explode(" ", $name); + + if (strlen($parts[0]) > 3 && $parts[0] !== "Sweetie" && $parts[0] !== "Filly" && $parts[0] !== "Windy" && (isset($parts[1]) && $parts[1] !== "Brightdawn" && $parts[1] !== "Fizz")) { + if ($parts[0] === "Princess") { + array_shift($parts); + } + + if (str_contains($parts[0], "/")) { + return explode("/", $parts[0])[0]; + } else { + return $parts[0]; + } + } else { + return $name; + } + } +} + +if (!function_exists("withCaretakersDown")) { + function withCaretakersDown(array $ordered): array { + return $ordered; + } +} + +if (!function_exists("getSystemMember")) { + function getSystemMember(string $system, string $id) { + $app = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/app.json"), true); + $systemID = $system; + + $members = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/" . ($systemID === $app["other"]["id"] ? "other" : $systemID) . "/members.json"), true); + $member = null; + + foreach ($members as $m) { + if ($m["id"] === $id) $member = $m; + } + + $member["system"] = $member["_system"] = $system; + + return $member; + } +} + +if (!function_exists("getMemberWithoutSystem")) { + function getMemberWithoutSystem(string $id) { + global $isLowerLoggedIn; global $isLoggedIn; + $member = null; + + $members1 = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc/members.json"), true); + foreach ($members1 as $m) { + $m["_system"] = "ynmuc"; + $m["system"] = "ynmuc"; + if ($m["id"] === $id) $member = $m; + } + + $members2 = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd/members.json"), true); + foreach ($members2 as $m) { + $m["_system"] = "gdapd"; + $m["system"] = "gdapd"; + if ($m["id"] === $id) $member = $m; + } + + if ($isLowerLoggedIn || $isLoggedIn) { + $app = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/app.json"), true); + $members3 = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/other/members.json"), true); + + foreach ($members3 as $m) { + $m["_system"] = $app["other"]["id"]; + $m["system"] = $app["other"]["id"]; + if ($m["id"] === $id) $member = $m; + } + } + + return $member; + } +} + +if (!function_exists("showMembersFromList")) { + function showMembersFromList(array $list) { + foreach ($list as $member) { if ($member['name'] !== "unknown" && $member['name'] !== "fusion") { + echo('<a href="/' . $member['name'] . '" style="text-decoration:none !important;filter:none !important;"><div class="hpd-item-card" style="background-color:rgba(255, 255, 255, .1);border:1px solid ' . (isset($member['color']) ? "#" . $member['color'] . "55" : "transparent") . ';outline-color:' . (isset($member['color']) ? "#" . $member['color'] . "55" : "transparent") . ';border-radius:10px;text-align:center;display:flex;align-items:center;justify-content:center;padding:5px;' . (isset($member["equestria"]) && $member["equestria"] ? 'opacity:.5;' : '') . '"><div> +<img alt="" src="' . getAsset($member["system"], $member["id"]) . '" style="border-radius:999px;background-color:rgba(0, 0, 0, .25);height:48px;width:48px;display:block;margin-left:auto;margin-right:auto;"> +<div style="text-decoration:none;color:white;margin-top:5px;">' . ($member['display_name'] ?? $member['name']) . '</div> +<div style="text-decoration:none !important;color:black !important;"><code style="text-decoration:none !important;color:white !important;">' . (isset($member['travelling']) && $member['travelling'] ? "+" . ($member['proxy_tags'][0]['prefix'] ?? " ") : ($member['proxy_tags'][0]['prefix'] ?? " ")) . '</code></div> +</div></div></a>'); + }} + } +} + +if (!function_exists("prettySize")) { + function prettySize($bytes) { + if ($bytes > 1024) { + if ($bytes > 1024**2) { + if ($bytes > 1024**3) { + return round($bytes / 1024**3, 1) . " GB"; + } else { + return round($bytes / 1024**2, 1) . " MB"; + } + } else { + return round($bytes / 1024, 1) . " KB"; + } + } else { + return $bytes . " B"; + } + } +} + +if (!function_exists("showSystem")) { + function showSystem(string $id, string $name, string $color, bool $hideTitle) { + $app = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/app.json"), true); + global $travelling; + + $global = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/" . (isset($app["other"]) && $id === $app["other"]["id"] ? "other" : $id) . "/general.json"), true); + + if ($hideTitle) { + echo('<div id="hpd-' . ($id === "gdapd" ? "raindrops" : ($id === "ynmuc" ? "cloudburst" : "other")) . '" style="background:rgba(255, 255, 255, .1);border-radius:10px;padding:10px;display:grid;grid-template-columns: 1fr;margin-bottom:10px;">'); + } else { + echo('<div id="hpd-' . ($id === "gdapd" ? "raindrops" : ($id === "ynmuc" ? "cloudburst" : "other")) . '" style="background:rgba(255, 255, 255, .1);border-radius:10px;padding:10px 10px 10px 20px;display:grid;grid-template-columns: 128px 1fr;margin-bottom:10px;">'); + } + if (!$hideTitle) echo('<a style="display:flex;margin: -10px -20px;align-items:center;justify-content:center;text-align:center;padding: 10px 20px;border-radius: 10px;background: #' . $global['color'] . '55;width: 148px;text-decoration:none;color:white;filter:none !important;" href="/' . ($id === "gdapd" ? "raindrops" : ($id === $app["other"]["id"] ? $app["other"]["slug"] : "cloudburst")) . '" class="hpd-system"> +<div style="text-align:center;"><img src="' . getAsset($id) . '" style="width:64px;"><br>' . $name . '</div> +</a>'); + + if ($hideTitle) { + echo(' <div style="display:grid;grid-template-columns:repeat(6, 1fr);grid-gap:10px;">'); + } else { + echo(' <div style="display:grid;grid-template-columns:repeat(6, 1fr);padding-left:10px;grid-gap:10px;">'); + } + + if ($id === $app["other"]["id"]) { + showMembersFromList(scoreOrder([...array_map(function ($i) use ($id, $travelling) { + $i["travelling"] = false; + $i["system"] = $id; + $i["equestria"] = false; + return $i; + }, json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/" . (isset($app["other"]) && $id === $app["other"]["id"] ? "other" : $id) . "/members.json"), true))], $id)); + } else { + showMembersFromList(scoreOrder([...array_map(function ($i) use ($id, $travelling) { + $i["travelling"] = false; + $i["system"] = $id; + $i["equestria"] = $travelling[$i['id']]['travelling'] && $travelling[$i['id']]['equestria']; + return $i; + }, array_filter(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/" . (isset($app["other"]) && $id === $app["other"]["id"] ? "other" : $id) . "/members.json"), true), function ($i) use ($travelling) { + return !(isset($travelling[$i['id']]) && $travelling[$i['id']]['travelling'] && (!isset($travelling[$i['id']]['equestria']) || !$travelling[$i['id']]['equestria'])); + })), ...array_map(function ($i) use ($id) { + $i["travelling"] = true; + $i["system"] = ($id === "gdapd" ? "ynmuc" : "gdapd"); + return $i; + }, array_filter(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/" . ($id === "gdapd" ? "ynmuc" : "gdapd") . "/members.json"), true), function ($i) use ($travelling) { + return isset($travelling[$i['id']]) && $travelling[$i['id']]['travelling'] && (!isset($travelling[$i['id']]['equestria']) || !$travelling[$i['id']]['equestria']); + }))], $id)); + } + + echo('</div> + +</div>'); + } +} + +if (!function_exists("cloudburst")) { + function cloudburst(bool $hideTitle): void { + showSystem("ynmuc", "Cloudburst System", "#5f08a9a6", $hideTitle); + } +} + + +if (!function_exists("raindrops")) { + function raindrops(bool $hideTitle): void { + showSystem("gdapd", "Raindrops System", "#a95f08a6", $hideTitle); + } +} + +if (!function_exists("other")) { + function other(bool $hideTitle): void { + $app = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/app.json"), true); + showSystem($app["other"]["id"], $app["other"]["name"], "#" . $app["other"]["color"] . "a6", $hideTitle); + } +} + +if (!function_exists("getMember")) { + function getMember(string $id) { + global $systemID; + + $members = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/$systemID/members.json"), true); + $member = null; + + foreach ($members as $m) { + if ($m["id"] === $id) $member = $m; + } + + return $member; + } +} + +if (!function_exists("timeAgo")) { + function timeAgo($time, $french = false): string { + if (!is_numeric($time)) { + $time = strtotime($time); + } + + $periods = ["sec", "min", "hr", "d", "wk", "mo", "y", "ages"]; + $periods_fr = $periods; + $lengths = array("60", "60", "24", "7", "4.35", "12", "100"); + + $now = time(); + + $difference = $now - $time; + if ($difference <= 10 && $difference >= 0) { + return $tense = "now"; + } elseif ($difference > 0) { + $tense = "ago"; + } else { + $tense = "later"; + } + + for ($j = 0; $difference >= $lengths[$j] && $j < count($lengths)-1; $j++) { + $difference /= $lengths[$j]; + } + + $difference = round($difference); + + $period = $periods[$j]; + return "{$difference} {$period} {$tense}"; + } +} + +if (!function_exists("timeIn")) { + function timeIn($time): string { + if (!is_numeric($time)) { + $time = strtotime($time); + } + + $periods = ["second", "minute", "hour", "day", "week", "month", "year", "age"]; + $lengths = array("60", "60", "24", "7", "4.35", "12", "100"); + + $now = time(); + + $difference = $time - $now; + if ($difference <= 10 && $difference >= 0) { + return $tense = "now"; + } elseif ($difference > 0) { + $tense = "in"; + } else { + $tense = "ago"; + } + + for ($j = 0; $difference >= $lengths[$j] && $j < count($lengths)-1; $j++) { + $difference /= $lengths[$j]; + } + + $difference = round($difference); + + $period = $periods[$j] . ($difference >1 ? "s" :''); + return "{$tense} {$difference} {$period}"; + } +} + +if (!function_exists("duration")) { + function duration($seconds) { + global $lang; global $pages; + + if ($seconds >= 60) { + if (floor($seconds / 60) >= 60) { + if (floor($seconds / 3600) >= 24) { + $days = floor($seconds / 86400); + + if ($lang["_french"]) { + return $days . " jour" . ($days > 1 ? "s" : ""); + } else { + return $days . " day" . ($days > 1 ? "s" : ""); + } + } else { + $hours = floor($seconds / 3600); + + if ($lang["_french"]) { + return $hours . " heure" . ($hours > 1 ? "s" : ""); + } else { + return $hours . " hour" . ($hours > 1 ? "s" : ""); + } + } + } else { + $minutes = floor($seconds / 60); + return $minutes . " minute" . ($minutes > 1 ? "s" : ""); + } + } else { + if ($lang["_french"]) { + return $seconds . " secondes"; + } else { + return $seconds . " seconds"; + } + } + } +} + +if (!function_exists("relativeDate")) { + function relativeDate($date, $showTime = true) { + if (!is_numeric($date)) $date = strtotime($date); + + if (!$showTime) { + if (date('Y-m-d', $date) === date('Y-m-d')) { + return "today"; + } elseif (date('Y-m-d', $date) === date('Y-m-d', time() + 86400)) { + return "tomorrow"; + } elseif ($date < time() + 518400) { + return date('l', $date); + } else { + return date('D j M', $date); + } + } else { + if (date('Y-m-d', $date) === date('Y-m-d')) { + return "today, <span class='time-adjust'>" . date('H:i', $date) . "</span>"; + } elseif (date('Y-m-d', $date) === date('Y-m-d', time() + 86400)) { + return "tomorrow, <span class='time-adjust'>" . date('H:i', $date) . "</span>"; + } elseif ($date < time() + 518400) { + return date('l', $date) . ", <span class='time-adjust'>" . date('H:i', $date) . "</span>"; + } else { + return date('D j M', $date) . ", <span class='time-adjust'>" . date('H:i', $date) . "</span>"; + } + } + } +} + +if (!function_exists("getMemberSystem")) { + function getMemberSystem(string $id) { + $list = scoreOrderGlobal(); + + foreach ($list as $item) { + if ($item["id"] === $id) return $item["_system"]; + } + } +} + +if (!function_exists("getMemberFromName")) { + function getMemberFromName(string $name) { + $list = [...json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd/members.json"), true), ...json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc/members.json"), true), ...json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/other/members.json"), true)]; + + foreach ($list as $item) { + if ($item["name"] === $name) return getMemberWithoutSystem($item["id"]); + } + } +} + +if (!function_exists("resolveMember")) { + function resolveMember(mixed $name) { + if (is_string($name)) { + if (str_ends_with($name, "-travelling")) { + return substr($name, 0, strlen($name) - 11); + } else { + return $name; + } + } else { + return $name; + } + } +}
\ No newline at end of file diff --git a/includes/util/homepage.inc b/includes/util/homepage.inc new file mode 100644 index 0000000..ebb99da --- /dev/null +++ b/includes/util/homepage.inc @@ -0,0 +1,36 @@ +<?php + +global $app; +global $travelling; + +function newHomepage($id, $page, $title) { + global $travelling; + $system = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/" . $id . "/general.json"), true); + + ?> + <div class="new-homepage-system" id="new-homepage-system-<?= $id ?>"> + <?php if ($title): ?> + <a href="/<?= $page ?>" style="height: 48px; display: flex; align-items: center; justify-content: center; border-top-left-radius: 10px; border-top-right-radius: 10px; text-align: center; text-decoration: none; color: white; background-color: #<?= $system["color"] ?? "000000" ?>55" class="new-homepage-system-title" id="new-homepage-system-<?= $id ?>-title"> + <div> + <img src="<?= getAsset($id) ?>" style="border-radius: 5px;width: 48px; vertical-align: middle;"><b style="vertical-align: middle; margin-left: 5px;"><?= $system["name"] ?></b> + </div> + </a> + <?php endif; ?> + + <div class="new-homepage-system-list" id="new-homepage-system-<?= $id ?>-list"> + <?php $list = array_values(scoreOrder(withTravelers(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/" . $id . "/members.json"), true), $id), $id)); foreach ($list as $index => $member): ?> + <a href="/<?= $member["name"] ?>" class="new-homepage-link <?= $index === count($list) - 1 ? "new-homepage-link-last" : "" ?>" style="color: white !important; text-decoration: none !important; display: block; background-color: #<?= $member["color"] ?>22; padding: 5px 10px;"> + <img src="<?= getAsset(($travelling[$member["id"]]["travelling"] && !$travelling[$member["id"]]["equestria"]) ? ($id === "gdapd" ? "ynmuc" : "gdapd") : $id, $member["id"]) ?>" style="width: 32px; height: 32px; vertical-align: middle; background-color: rgba(0, 0, 0, .25); border-radius: 999px;"> + <span style="width: calc(100% - 37px); vertical-align: middle; margin-left: 5px;text-overflow: ellipsis;white-space: nowrap;overflow: hidden;"><?= $member["display_name"] ?? $member["name"] ?></span> + <span style="vertical-align: middle; float:right;"> + <?php if ($travelling[$member["id"]]["travelling"] && $travelling[$member["id"]]["equestria"]): ?> + <img src="/assets/logo/equestria.png" style="width: 24px; height: 24px; margin-top: 5px;"> + <?php elseif ($travelling[$member["id"]]["travelling"]): ?> + <img src="<?= getAsset($id === "gdapd" ? "ynmuc" : "gdapd") ?>" style="width: 24px; height: 24px; margin-top: 5px;"> + <?php endif; ?> + </span> + </a> + <?php endforeach; ?> + </div> + </div> +<?php }
\ No newline at end of file diff --git a/includes/util/language.inc b/includes/util/language.inc new file mode 100644 index 0000000..e24255f --- /dev/null +++ b/includes/util/language.inc @@ -0,0 +1,3 @@ +<?php + +$lang = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/i18n/en.json"), true);
\ No newline at end of file diff --git a/includes/util/profiles.inc b/includes/util/profiles.inc new file mode 100644 index 0000000..66274c7 --- /dev/null +++ b/includes/util/profiles.inc @@ -0,0 +1,97 @@ +<?php + +function calculateProfileScore($member) { + $values = [ + ((isset($member["_metadata"]["birth"]["age"]) && $member["_metadata"]["birth"]["age"] > 0) || isset($member["_metadata"]["birth"]["year"]) && $member["_metadata"]["birth"]["year"] > 1900) || ((isset($member["_metadata"]["birth"]["age"]) && $member["_metadata"]["birth"]["age"] === -1) && in_array("alicorn", $member["_metadata"]["species"])), + isset($member["_metadata"]["birth"]["date"]) && trim($member["_metadata"]["birth"]["date"]) !== "" && $member["_metadata"]["birth"]["date"] !== "01-01", + file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/" . $member["id"] . ".html") && strlen(preg_replace("/[^a-zA-Z0-9]/m", "", file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/" . $member["id"] . ".html"))) > 200, + file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/" . $member["id"] . "-private.html") && strlen(preg_replace("/[^a-zA-Z0-9]/m", "", file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/" . $member["id"] . "-private.html"))) > 200, + isset($member["banner"]), + file_exists($_SERVER['DOCUMENT_ROOT'] . "/assets/uploads/pt-" . $member["name"] . ".png"), + isset($member["color"]) + ]; + + $result = [ + "values" => $values, + "pages" => [], + "characters" => [], + "score" => (float)array_reduce($values, function ($a, $b) { + return $a + $b; + }), + "score2" => (float)array_reduce($values, function ($a, $b) { + return $a + $b; + }), + "actions" => [] + ]; + + if (file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/" . $member["id"] . ".html") && strlen(preg_replace("/[^a-zA-Z0-9]/m", "", file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/" . $member["id"] . ".html"))) <= 200) { + $result["pages"][] = true; + $result["score"] += strlen(preg_replace("/[^a-zA-Z0-9]/m", "", file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/" . $member["id"] . ".html"))) / 201; + } else { + $result["pages"][] = false; + } + + if (file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/" . $member["id"] . "-private.html") && strlen(preg_replace("/[^a-zA-Z0-9]/m", "", file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/" . $member["id"] . "-private.html"))) <= 200) { + $result["pages"][] = true; + $result["score"] += strlen(preg_replace("/[^a-zA-Z0-9]/m", "", file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/" . $member["id"] . "-private.html"))) / 201; + } else { + $result["pages"][] = false; + } + + if (file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/" . $member["id"] . ".html")) { + $result["characters"][] = strlen(preg_replace("/[^a-zA-Z0-9]/m", "", file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/" . $member["id"] . ".html"))); + } else { + $result["characters"][] = -1; + } + + if (file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/" . $member["id"] . "-private.html")) { + $result["characters"][] = strlen(preg_replace("/[^a-zA-Z0-9]/m", "", file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/content/" . $member["id"] . "-private.html"))); + } else { + $result["characters"][] = -1; + } + + if (!((isset($member["_metadata"]["birth"]["age"]) && $member["_metadata"]["birth"]["age"] > 0) || isset($member["_metadata"]["birth"]["year"]) && $member["_metadata"]["birth"]["year"] > 1900) && !(isset($member["_metadata"]["birth"]["age"]) && $member["_metadata"]["birth"]["age"] === -1) && in_array("alicorn", $member["_metadata"]["species"])) { + $result["score"] += 0.5; + } + + $result["progress"] = $result["score"] / 7; + $result["sortable"] = (int)($result["progress"] * 100000000000000); + + if (!$result["values"][5]) { + $result["actions"][] = "a Pony Town character"; + } + + if (!$result["values"][6]) { + $result["actions"][] = "a color"; + } + + if (!$result["values"][2]) { + if ($result["characters"][0] === -1) { + $result["actions"][] = "a public page"; + } else { + $result["actions"][] = (200 - $result["characters"][0]) . " characters to the public page"; + } + } + + if (!$result["values"][3]) { + if ($result["characters"][1] === -1) { + $result["actions"][] = "a private page"; + } else { + $result["actions"][] = (200 - $result["characters"][1]) . " characters to the private page"; + } + } + + if (!$result["values"][4]) { + $result["actions"][] = "a banner"; + } + + if (!$result["values"][0]) { + $result["actions"][] = "an age"; + } + + if (!$result["values"][1]) { + $result["actions"][] = "a birthdate"; + } + + return $result; +}
\ No newline at end of file diff --git a/includes/util/pronouns.inc b/includes/util/pronouns.inc new file mode 100644 index 0000000..6ab2487 --- /dev/null +++ b/includes/util/pronouns.inc @@ -0,0 +1,149 @@ +<?php + +$pronounsSets = [ + "pony" => [ + "gender" => "ponygender", + "gender:fr" => "poneygenre", + "object" => "pony", + "person" => "pony", + "possessive_det" => "pony's", + "possessive_pro" => "pony's", + "reflexive" => "ponyself", + "subjective" => "pony", + "third" => true, + "color" => "warning" + ], + "she" => [ + "gender" => "female", + "gender:fr" => "fille", + "object" => "her", + "person" => "girl", + "possessive_det" => "her", + "possessive_pro" => "hers", + "reflexive" => "herself", + "subjective" => "she", + "third" => true, + "color" => "success" + ], + "he" => [ + "gender" => "male", + "gender:fr" => "garçon", + "object" => "him", + "person" => "boy", + "possessive_det" => "his", + "possessive_pro" => "his", + "reflexive" => "himself", + "subjective" => "he", + "third" => true, + "color" => "info" + ], + "it" => [ + "gender" => "agender", + "gender:fr" => "agenre", + "object" => "it", + "person" => "person", + "possessive_det" => "its", + "possessive_pro" => "its", + "reflexive" => "itself", + "subjective" => "it", + "third" => true, + "color" => "light" + ], + "they" => [ + "gender" => "non binary", + "gender:fr" => "non binaire", + "object" => "them", + "person" => "person", + "possessive_det" => "their", + "possessive_pro" => "theirs", + "reflexive" => "themself", + "subjective" => "they", + "third" => false, + "color" => "primary" + ] +]; + +$pronounGetCount = 0; + +$possibilitiesPerSet = []; +foreach ($pronounsSets as $name => $set) { + if (!isset($possibilitiesPerSet[$name])) $possibilitiesPerSet[$name] = []; + $possibilitiesPerSet[$name][] = $name; + + foreach ($set as $category => $value) { + if (is_string($value)) $possibilitiesPerSet[$name][] = $value; + } +} + +function getSetFromValue(string $value) { + global $possibilitiesPerSet; + + foreach ($possibilitiesPerSet as $name => $set) { + if (in_array($value, $set)) { + return $name; + } + } + + return null; +} + +function getPronounsFromMark(?string $mark = null): array { + if (!isset($mark) || trim($mark) === "") { + return ["they"]; + } else { + $parts = array_unique(array_map(function ($i) { + return getSetFromValue($i); + }, explode("/", $mark))); + return $parts; + } +} + +function getMemberPronouns(?string $pronouns): ?array { + global $pronounsSets; + $list = getPronounsFromMark($pronouns); + return $pronounsSets[$list[array_rand($list)]] ?? $pronounsSets["she"]; +} + +function getGenderFromPronoun(string $pronoun, bool $french = false) { + global $pronounsSets; + $set = getPronounsFromMark($pronoun)[0]; + + if ($french) { + return ($pronounsSets[$set] ?? $pronounsSets["they"])["gender:fr"]; + } else { + return ($pronounsSets[$set] ?? $pronounsSets["they"])["gender"]; + } +} + +function pronounInFrench(string $pronoun): string { + return match ($pronoun) { + "she" => "elle", + "her" => "elle", + "he" => "il", + "him" => "lui", + "they" => "iel", + "them" => "iel", + "it" => "iel", + "its" => "iel", + "pony" => "poney", + "pony's" => "à poney", + "ponys" => "à poney", + default => $pronoun, + }; +} + +function getTooltipsFromMark(string $mark = null, bool $french = false): ?string { + if (!isset($mark)) { + return null; + } else { + if ($french) { + return implode("/", array_map(function ($i) { + return "<span title='" . ucfirst(getGenderFromPronoun($i, true)) . "' data-bs-toggle='tooltip'>" . pronounInFrench($i) . "</span>"; + }, explode("/", $mark))); + } else { + return implode("/", array_map(function ($i) { + return "<span title='" . ucfirst(getGenderFromPronoun($i, false)) . "' data-bs-toggle='tooltip'>" . $i . "</span>"; + }, explode("/", $mark))); + } + } +}
\ No newline at end of file diff --git a/includes/util/rainbow.inc b/includes/util/rainbow.inc new file mode 100644 index 0000000..66ac2fc --- /dev/null +++ b/includes/util/rainbow.inc @@ -0,0 +1,56 @@ +<?php + +require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/functions.inc"; +require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/bitset.inc"; +require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/score.inc"; + +function rainbow($hideCloudburst = false): array { + $members = scoreOrderGlobal(); + + if ($hideCloudburst) { + $members = array_filter($members, function ($i) { + return $i["_system"] === "gdapd"; + }); + } + + $data = []; + + foreach ($members as $member) { + $data[$member["name"]] = [ + "_data" => $member + ]; + + if (isset($member["color"])) { + $data[$member["name"]]["rgb"] = [ + hexdec(substr($member["color"], 0, 2)), + hexdec(substr($member["color"], 2, 2)), + hexdec(substr($member["color"], 4, 2)) + ]; + $data[$member["name"]]["hsl"] = rgbToHsl( + $data[$member["name"]]["rgb"][0], + $data[$member["name"]]["rgb"][1], + $data[$member["name"]]["rgb"][2] + ); + } else { + $data[$member["name"]]["rgb"] = [255, 255, 255]; + $data[$member["name"]]["hsl"] = rgbToHsl(255, 255, 255); + } + } + + return $data; +} + +function getMembersByColor($hideCloudburst = false): array { + $members = rainbow($hideCloudburst); + uasort($members, function ($a, $b) { + return $a['hsl'][0] - $b['hsl'][0]; + }); + + $sorted = []; + foreach ($members as $data) { + $data["_data"]["hue"] = $data["hsl"][0]; + $sorted[] = $data["_data"]; + } + + return $sorted; +}
\ No newline at end of file diff --git a/includes/util/random.inc b/includes/util/random.inc new file mode 100644 index 0000000..b6e7905 --- /dev/null +++ b/includes/util/random.inc @@ -0,0 +1,15 @@ +<?php + +/** + * @throws Exception + */ +function random($length = 13): string { + if (function_exists("random_bytes")) { + $bytes = random_bytes(ceil($length / 2)); + } elseif (function_exists("openssl_random_pseudo_bytes")) { + $bytes = openssl_random_pseudo_bytes(ceil($length / 2)); + } else { + throw new Exception("No cryptographically secure random function available"); + } + return substr(bin2hex($bytes), 0, $length); +}
\ No newline at end of file diff --git a/includes/util/score.inc b/includes/util/score.inc new file mode 100644 index 0000000..57c7a75 --- /dev/null +++ b/includes/util/score.inc @@ -0,0 +1,136 @@ +<?php + +function scoreOrder($members, $system, $useDominant = false) { + $ordered = []; + foreach ($members as $member) { + if ($member["name"] !== "unknown" && $member["name"] !== "fusion" && $member["name"] !== "new" && !str_starts_with($member["name"], "smol") && !str_ends_with($member["name"], "-travelling") && file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/metadata/$member[id].json")) { + if (isset($member["color"])) { + if ($useDominant) { + $rgb = [ + hexdec(substr($member["dominant_color"] ?? $member["color"], 0, 2)), + hexdec(substr($member["dominant_color"] ?? $member["color"], 2, 2)), + hexdec(substr($member["dominant_color"] ?? $member["color"], 4, 2)) + ]; + } else { + $rgb = [ + hexdec(substr($member["color"], 0, 2)), + hexdec(substr($member["color"], 2, 2)), + hexdec(substr($member["color"], 4, 2)) + ]; + } + + $hsl = rgbToHsl( + $rgb[0], + $rgb[1], + $rgb[2] + ); + } else { + $rgb = [255, 255, 255]; + $hsl = rgbToHsl(255, 255, 255); + } + + $systemID = $member["system"] ?? $system; + + $member["_system"] = $member["system"] = $systemID; + $member["_metadata"] = parseMetadata(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/metadata/$member[id].json"), true)); + $member["_score"] = $hsl[0]; + $ordered[] = $member; + } + } + + uasort($ordered, function($a, $b) { + return $a["_score"] - $b["_score"]; + }); + + return $ordered; +} + +function scoreOrderGlobal() { + global $isLowerLoggedIn; + global $isLoggedIn; + + $ordered = []; + foreach (json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd/members.json"), true) as $member) { + if ($member["name"] !== "unknown" && $member["name"] !== "fusion" && $member["name"] !== "new" && !str_starts_with($member["name"], "smol") && !str_ends_with($member["name"], "-travelling") && file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/metadata/$member[id].json")) { + if (isset($member["color"])) { + $rgb = [ + hexdec(substr($member["color"], 0, 2)), + hexdec(substr($member["color"], 2, 2)), + hexdec(substr($member["color"], 4, 2)) + ]; + $hsl = rgbToHsl( + $rgb[0], + $rgb[1], + $rgb[2] + ); + } else { + $rgb = [255, 255, 255]; + $hsl = rgbToHsl(255, 255, 255); + } + $member["_score"] = $hsl[0]; + $member["_system"] = "gdapd"; + $member["system"] = "gdapd"; + $member["_metadata"] = parseMetadata(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/metadata/$member[id].json"), true)); + $ordered[] = $member; + } + } + + foreach (json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc/members.json"), true) as $member) { + if ($member["name"] !== "unknown" && $member["name"] !== "fusion" && $member["name"] !== "new" && !str_starts_with($member["name"], "smol") && !str_ends_with($member["name"], "-travelling") && file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/metadata/$member[id].json")) { + if (isset($member["color"])) { + $rgb = [ + hexdec(substr($member["color"], 0, 2)), + hexdec(substr($member["color"], 2, 2)), + hexdec(substr($member["color"], 4, 2)) + ]; + $hsl = rgbToHsl( + $rgb[0], + $rgb[1], + $rgb[2] + ); + } else { + $rgb = [255, 255, 255]; + $hsl = rgbToHsl(255, 255, 255); + } + $member["_score"] = $hsl[0]; + $member["_system"] = "ynmuc"; + $member["system"] = "ynmuc"; + $member["_metadata"] = parseMetadata(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/metadata/$member[id].json"), true)); + $ordered[] = $member; + } + } + + if ($isLowerLoggedIn || $isLoggedIn) { + $app = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/app.json"), true); + foreach (json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/other/members.json"), true) as $member) { + if ($member["name"] !== "unknown" && $member["name"] !== "fusion" && $member["name"] !== "new" && !str_starts_with($member["name"], "smol") && !str_ends_with($member["name"], "-travelling") && file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/data/metadata/$member[id].json")) { + if (isset($member["color"])) { + $rgb = [ + hexdec(substr($member["color"], 0, 2)), + hexdec(substr($member["color"], 2, 2)), + hexdec(substr($member["color"], 4, 2)) + ]; + $hsl = rgbToHsl( + $rgb[0], + $rgb[1], + $rgb[2] + ); + } else { + $rgb = [255, 255, 255]; + $hsl = rgbToHsl(255, 255, 255); + } + $member["_score"] = $hsl[0]; + $member["_system"] = $app["other"]["id"]; + $member["system"] = $app["other"]["id"]; + $member["_metadata"] = parseMetadata(json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/metadata/$member[id].json"), true)); + $ordered[] = $member; + } + } + } + + uasort($ordered, function($a, $b) { + return $a["_score"] - $b["_score"]; + }); + + return array_values($ordered); +}
\ No newline at end of file diff --git a/includes/util/session.inc b/includes/util/session.inc new file mode 100644 index 0000000..7a6b931 --- /dev/null +++ b/includes/util/session.inc @@ -0,0 +1,29 @@ +<?php + +require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/functions.inc"; + +global $isLoggedIn; +global $isLowerLoggedIn; +global $_PROFILE; + +$isLoggedIn = false; +$isLowerLoggedIn = false; + +if (isset($_COOKIE['PEH2_SESSION_TOKEN'])) { + if (!(str_contains($_COOKIE['PEH2_SESSION_TOKEN'], ".") || str_contains($_COOKIE['PEH2_SESSION_TOKEN'], "/") || trim($_COOKIE["PEH2_SESSION_TOKEN"]) === "")) { + if (file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . str_replace(".", "", str_replace("/", "", $_COOKIE['PEH2_SESSION_TOKEN'])))) { + $_PROFILE = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/tokens/" . str_replace(".", "", str_replace("/", "", $_COOKIE['PEH2_SESSION_TOKEN']))), true); + + if (isset($_GET['invert'])) { + $_PROFILE["login"] = $_PROFILE["login"] === "raindrops" ? "cloudburst" : "raindrops"; + $_PROFILE["name"] = $_PROFILE["name"] === "Raindrops System" ? "Cloudburst System" : "Raindrops System"; + } + + $isLoggedIn = true; + } elseif (file_exists($_SERVER['DOCUMENT_ROOT'] . "/includes/lowertokens/" . str_replace(".", "", str_replace("/", "", $_COOKIE['PEH2_SESSION_TOKEN'])))) { + $_PROFILE = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/lowertokens/" . str_replace(".", "", str_replace("/", "", $_COOKIE['PEH2_SESSION_TOKEN']))), true); + + $isLowerLoggedIn = true; + } + } +}
\ No newline at end of file diff --git a/includes/util/short.inc b/includes/util/short.inc new file mode 100644 index 0000000..39e42b0 --- /dev/null +++ b/includes/util/short.inc @@ -0,0 +1,55 @@ +<?php + +require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/functions.inc"; +require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/bitset.inc"; +require_once $_SERVER['DOCUMENT_ROOT'] . "/includes/util/score.inc"; + +global $toplevel; global $lang; global $pages; + +header("Content-Type: text/plain"); +$members = scoreOrderGlobal(); +$list = [ + "rd" => "/raindrops", + "cb" => "/cloudburst", + "minty" => "/cloudydreams", + "twilight" => "/twi", + "luna" => "/princessluna", + "cloudy" => "/cloudydreams", + "zipp" => "/zippstorm", + "babs" => "/babsseed", + "frost" => "/frostcrystals", + "violet" => "/violetdawn" +]; + +foreach ($members as $member) { + for ($i = 1; $i < strlen($member["name"]); $i++) { + $part = substr($member["name"], 0, $i); + + if (in_array($part, array_keys($list))) { + $list[$part] = false; + } else { + $list[$part] = "/" . $member["name"]; + } + } + + foreach ($member["proxy_tags"] as $proxy) { + $system = $member["_system"] === "gdapd" ? "rd" : "cb"; + $list[$system . preg_replace("/[^a-z]/m", "", $proxy["prefix"])] = "/" . $member["name"]; + } + + $list[$member["id"]] = "/" . $member["name"]; + $list[$member["uuid"]] = "/" . $member["name"]; +} + +$list["minty"] = "/cloudydreams"; +$list["twilight"] = "/twi"; + +if (in_array($toplevel, array_keys($list)) && $list[$toplevel]) { + if ($toplevel !== "unknown") { + header("Location: " . $list[$toplevel]); + } else { + peh_error("Page not found: " . strip_tags($toplevel), 404); + } +} else { + peh_error("Page not found: " . strip_tags($toplevel), 404); +}
\ No newline at end of file diff --git a/includes/util/travelling.inc b/includes/util/travelling.inc new file mode 100644 index 0000000..0d1696a --- /dev/null +++ b/includes/util/travelling.inc @@ -0,0 +1,62 @@ +<?php + +$app = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/app.json"), true); +$travelling = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/travelling/travelling.json"), true); + +$json_cloudburst = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/ynmuc/members.json"), true); +$json_raindrops = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/gdapd/members.json"), true); +$json_other = json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/other/members.json"), true); + +if (!isset($json_cloudburst)) $json_cloudburst = []; +if (!isset($json_raindrops)) $json_raindrops = []; +if (!isset($json_other)) $json_other = []; + +$members = [...array_map(function ($i) { + $i["_system"] = "ynmuc"; + return $i; +}, $json_cloudburst), ...array_map(function ($i) { + $i["_system"] = "gdapd"; + return $i; +}, $json_raindrops), ...array_map(function ($i) use ($app) { + $i["_system"] = $app["other"]["id"]; + return $i; +}, $json_other)]; + +foreach ($members as $member) { + if (!isset($travelling[$member["id"]])) { + $travelling[$member["id"]] = [ + "travelling" => false, + "history" => [] + ]; + + @file_put_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/travelling/travelling.json", utf8_encode(json_encode($travelling, JSON_PRETTY_PRINT))); + } + + if (!isset($travelling[$member["id"]]["equestria"])) { + $travelling[$member["id"]]["equestria"] = false; + } +} + +function withTravelers(array $members, string $system): array { + global $travelling; + global $app; + + if ($system === $app["other"]["id"]) { + return $members; + } else { + return [ + ...array_map(function ($i) use ($system) { + $i['system'] = $system; + return $i; + }, array_filter($members, function ($i) use ($travelling) { + return !(isset($travelling[$i['id']]) && $travelling[$i['id']]['travelling'] && (!isset($travelling[$i['id']]['equestria']) || !$travelling[$i['id']]['equestria'])); + })), + ...array_filter(array_map(function ($i) use ($system) { + $i['system'] = $system === "gdapd" ? "ynmuc" : "gdapd"; + return $i; + }, json_decode(file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/includes/data/" . ($system === "gdapd" ? "ynmuc" : "gdapd") . "/members.json"), true)), function ($i) use ($travelling) { + return isset($travelling[$i['id']]) && $travelling[$i['id']]['travelling'] && (!isset($travelling[$i['id']]['equestria']) || !$travelling[$i['id']]['equestria']); + }) + ]; + } +}
\ No newline at end of file |