├── README.md ├── flarum.ext.css ├── flarum └── core │ ├── src │ ├── Api │ │ └── JsonApiResponse.php │ └── Locale │ │ └── LocaleServiceProvider.php │ └── views │ └── frontend │ └── app.blade.php ├── fof └── upload │ ├── resources │ └── templates │ │ ├── audio.blade.php │ │ ├── image.blade.php │ │ ├── text.blade.php │ │ └── video.blade.php │ └── src │ ├── Providers │ └── DownloadProvider.php │ └── Templates │ ├── AudioTemplate.php │ ├── TextTemplate.php │ └── VideoTemplate.php └── hack.sh /README.md: -------------------------------------------------------------------------------- 1 | ## 用於 [vivaldi.club](https://vivaldi.club) 的一些 hack 2 | 3 | ### 需求 4 | 5 | - Flarum v0.1.0-beta.12 6 | 7 | ### HACK 8 | 9 | - 增加介面語言自動適配 10 | - 增加簡繁自動轉換 11 | - 增加中日韓字符註冊支援 12 | - 增加 @ 中日韓用戶名支援 13 | - 增加用戶頭像彩色邊框 14 | - 增加用戶 UID 展示 15 | - 增加用戶 UID 至 PostStream 屬性 16 | - 增加發帖人 UserAgent 展示 17 | - 增加 Vivaldi 用戶 PO 文專屬標記 18 | - 增加 [vivaldi://](vivaldi://about/) scheme 支援 19 | - 移除用戶名最小長度限制 20 | - 移除標題最小字數限制 21 | - 移除貼文字數限制 22 | - 移除論壇創建人灌水限制 23 | - 移除首頁節點列表中的次節點 24 | - 移除固頂貼預覽 25 | - 移除論壇連結 slug 26 | - 更改相對時間為絕對時間 27 | - 更改英文數位記法為中文數位記法 28 | - 更改用戶資料頁連結中的用戶名為 UID 29 | - 更改 font-awesome 加載位置 30 | - 更改 [reflar/level-ranks](https://github.com/reflar/level-ranks) 的計算方式,與 [antoinefr/flarum-ext-money](https://github.com/antoinefr/flarum-ext-money) 保持一致 31 | - 更改 [reflar/level-ranks](https://github.com/reflar/level-ranks) 升級經驗算法為 log(n) 32 | - 更改 [flagrow/upload](https://github.com/flagrow/upload) 文件大小為二進位前綴 33 | - 更改 [flagrow/upload](https://github.com/flagrow/upload) 異常提示,增加 MimeType 34 | - 更改 [flagrow/sitemap](https://github.com/flagrow/sitemap) 連結格式 35 | - 阻止 [flarum/pusher](https://github.com/flarum/pusher) 啟用後隱藏刷新按鈕 36 | - 阻止 [flagrow/split](https://github.com/flagrow/split) 生成 slug 37 | - 阻止 [fof/secure-https](https://github.com/FriendsOfFlarum/secure-https) 代理 HTTPS 內容 38 | - 客制 [BBCode](https://github.com/Csineneo/vivaldi-club-bbcode) 39 | - 客制 [flarum/core](https://github.com/flarum/core) 頁面模板 40 | - 客制 [flagrow/upload](https://github.com/flagrow/upload) 內容展示模板 41 | 42 | ### 演示站點 43 | 44 | - [Vivaldi Club](https://vivaldi.club) 45 | -------------------------------------------------------------------------------- /flarum.ext.css: -------------------------------------------------------------------------------- 1 | /* webfonts */ 2 | /* Font Awesome */ 3 | @font-face { 4 | font-family: "FAB"; 5 | font-style: normal; 6 | font-weight: normal; 7 | src: url(../fonts/fa-brands-400.eot); 8 | src: url(../fonts/fa-brands-400.eot?#iefix) format("embedded-opentype"), 9 | url(../fonts/fa-brands-400.woff2) format("woff2"), 10 | url(../fonts/fa-brands-400.woff) format("woff"), 11 | url(../fonts/fa-brands-400.ttf) format("truetype"), 12 | url(../fonts/fa-brands-400.svg#fontawesome) format("svg") 13 | } 14 | @font-face { 15 | font-family: "FAS"; 16 | font-style: normal; 17 | font-weight: 900; 18 | src: url(../fonts/fa-solid-900.eot); 19 | src: url(../fonts/fa-solid-900.eot?#iefix) format("embedded-opentype"), 20 | url(../fonts/fa-solid-900.woff2) format("woff2"), 21 | url(../fonts/fa-solid-900.woff) format("woff"), 22 | url(../fonts/fa-solid-900.ttf) format("truetype"), 23 | url(../fonts/fa-solid-900.svg#fontawesome) format("svg") 24 | } 25 | .fa { font-family: FAB, FAS; } 26 | .fab { font-family: FAB; } 27 | .fas { font-family: FAS; font-weight: 900; } 28 | 29 | /* latin-ext */ 30 | @font-face { 31 | font-family: 'Source Code Pro'; 32 | font-style: normal; 33 | font-weight: 400; 34 | src: local('Source Code Pro'), local('SourceCodePro-Regular'), url(https://fonts.gstatic.com/s/sourcecodepro/v8/HI_SiYsKILxRpg3hIP6sJ7fM7PqlM-vWnsUnxlC9.woff2) format('woff2'); 35 | unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; 36 | } 37 | /* latin */ 38 | @font-face { 39 | font-family: 'Source Code Pro'; 40 | font-style: normal; 41 | font-weight: 400; 42 | src: local('Source Code Pro'), local('SourceCodePro-Regular'), url(https://fonts.gstatic.com/s/sourcecodepro/v8/HI_SiYsKILxRpg3hIP6sJ7fM7PqlPevWnsUnxg.woff2) format('woff2'); 43 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; 44 | } 45 | /* latin-ext */ 46 | @font-face { 47 | font-family: 'Raleway'; 48 | font-style: normal; 49 | font-weight: 400; 50 | src: local('Raleway'), local('Raleway-Regular'), url(https://fonts.gstatic.com/s/raleway/v12/1Ptug8zYS_SKggPNyCMIT4ttDfCmxA.woff2) format('woff2'); 51 | unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; 52 | } 53 | /* latin */ 54 | @font-face { 55 | font-family: 'Raleway'; 56 | font-style: normal; 57 | font-weight: 400; 58 | src: local('Raleway'), local('Raleway-Regular'), url(https://fonts.gstatic.com/s/raleway/v12/1Ptug8zYS_SKggPNyC0IT4ttDfA.woff2) format('woff2'); 59 | unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; 60 | } 61 | 62 | * { tab-size: 4; } 63 | :target { background-color: #e3f5ff; } 64 | ::selection { color: #f3f; } 65 | body { font-family: Raleway, FAB, FAS, 'System Latin'; font-size: 15px; line-height: 1.5em; text-rendering: optimizeLegibility; -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: subpixel-antialiased; -webkit-tap-highlight-color: rgba(158, 217, 248, .3); } 66 | code { white-space: pre-wrap; font-family: 'Source Code Pro', 'System Mono'; } 67 | li.item-joined, li.item-lastSeen { display: block; line-height: 1.7; } 68 | li.item-lastSeen .online { display: inline-block; } 69 | input.FormControl { color: #998f66 !important; } 70 | small { color: transparent; font-size: 7px; line-height: 1em; width: 100%; margin-top: 20px; word-break: break-all; clear: both; display: block; overflow: hidden; text-overflow: ellipsis; white-space: pre-wrap; } 71 | small:hover { color: #f3f; } 72 | #footer { margin: 1em auto; padding-top: 3em; text-align: center; color: #7c7c7c; width: 61.8%;} 73 | .viv-icon { float: right; width: 150px; box-shadow: 0 0 1px #ccc; } 74 | .tofu-snapshot { margin-bottom: 2em; padding: 12px 16px; color: #ad6c00; background: #fff2ae; line-height: 1.5; border-radius: 4px; } 75 | .Badge--group--10 { display: none !important; } 76 | .DiscussionList-loadMore .Button { width: 100%; } 77 | .Post { padding-bottom: 3px; } 78 | .Post-footer { margin-bottom: 0; } 79 | .Post-mentionedBy-preview { width: 640px !important; } 80 | .PostUser-level { margin-bottom: 20px; } 81 | .Search-input input { width: 125px; } 82 | .TagsPage .item-flagrow-ad { display: none !important; } 83 | .item-discussion-views { width: 33px; margin-right: -70px; } 84 | .item-discussion-views::before { margin-left: -22px; } 85 | .item-terminalPost { font-size: 13px; } 86 | .item-money:before { content: "\f530"; } 87 | .uid-1 { border-color: #e03c8a !important; } 88 | .upl-image-tpl { border: 1px solid #CB1B4567; margin: 16px; } 89 | .upl-image-tpl figcaption { background-color: #CB1B45; color: #fff; height: 2em; line-height: 2em; padding: 0 .5em; } 90 | .upl-image-title i { margin-right: .3em; } 91 | .upl-image-size i { margin: 0 .3em 0 1em; } 92 | .upl-image-link { float: right; } 93 | .upl-image-link a { color: #fff; border-bottom: none; } 94 | 95 | /* Dark Mode */ 96 | body.dark .Dropdown-menu > li > a:hover, 97 | body.dark .Dropdown-menu > li > button:hover, 98 | { color: #CB1B45 } 99 | body.dark .Dropdown-menu > li > a, 100 | body.dark .Dropdown-menu > li > button, 101 | body.dark .DiscussionListItem-count 102 | { background-color: transparent !important; } 103 | body.dark .DiscussionListItem-title { color: #ccc; } 104 | 105 | /* Tag Icon */ 106 | .TagHero .Hero-title:before, .TagLabel > span:before { height: 22px; margin-right: 0.3em; display: inline-block; font-family: FAS; font-size: 85%; text-rendering: auto; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } 107 | .TagLinkButton > .TagIcon { width: 18px !important; height: 18px !important; } 108 | .TagLinkButton > span:before { color: #fff; height: 22px; display: block; font-family: FAS; font-size: 85%; text-rendering: auto; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } 109 | /* vivaldi */ 110 | .TagHero[style*='rgb(203, 27, 69)'] .Hero-title:before, 111 | .TagLabel[style*='rgb(203, 27, 69)'] > span:before, 112 | .TagLinkButton span[style*='rgb(203, 27, 69)']:before 113 | { content: "\f004"; } 114 | /* browser */ 115 | .TagHero[style*='rgb(88, 178, 220)'] .Hero-title:before, 116 | .TagLabel[style*='rgb(88, 178, 220)'] > span:before, 117 | .TagLinkButton span[style*='rgb(88, 178, 220)']:before 118 | { content: "\f268"; font-family: FAB; } 119 | /* opera */ 120 | .TagHero[style*='rgb(255, 75, 75)'] .Hero-title:before, 121 | .TagLabel[style*='rgb(255, 75, 75)'] > span:before, 122 | .TagLinkButton span[style*='rgb(255, 75, 75)']:before 123 | { content: "\f26a"; font-family: FAB; } 124 | /* flarum*/ 125 | .TagHero[style*='rgb(231, 103, 46)'] .Hero-title:before, 126 | .TagLabel[style*='rgb(231, 103, 46)'] > span:before, 127 | .TagLinkButton span[style*='rgb(231, 103, 46)']:before 128 | { content: "\f120"; } 129 | /* robot */ 130 | .TagHero[style*='rgb(178, 143, 206)'] .Hero-title:before, 131 | .TagLabel[style*='rgb(178, 143, 206)'] > span:before, 132 | .TagLinkButton span[style*='rgb(178, 143, 206)']:before 133 | { content: "\f17b"; font-family: FAB; } 134 | /* inner */ 135 | .TagHero[style*='rgb(67, 67, 67)'] .Hero-title:before, 136 | .TagLabel[style*='rgb(67, 67, 67)'] > span:before, 137 | .TagLinkButton span[style*='rgb(67, 67, 67)']:before 138 | { content: "\f21b"; } 139 | /* pool */ 140 | .TagHero[style*='rgb(102, 186, 183)'] .Hero-title:before, 141 | .TagLabel[style*='rgb(102, 186, 183)'] > span:before, 142 | .TagLinkButton span[style*='rgb(102, 186, 183)']:before 143 | { content: "\f0f4"; } 144 | /* share */ 145 | .TagHero[style*='rgb(241, 124, 103)'] .Hero-title:before, 146 | .TagLabel[style*='rgb(241, 124, 103)'] > span:before, 147 | .TagLinkButton span[style*='rgb(241, 124, 103)']:before 148 | { content: "\f06b"; } 149 | /* help */ 150 | .TagHero[style*='rgb(202, 122, 44)'] .Hero-title:before, 151 | .TagLabel[style*='rgb(202, 122, 44)'] > span:before, 152 | .TagLinkButton span[style*='rgb(202, 122, 44)']:before 153 | { content: "\f059"; } 154 | /* hardware */ 155 | .TagHero[style*='rgb(134, 193, 102)'] .Hero-title:before, 156 | .TagLabel[style*='rgb(134, 193, 102)'] > span:before, 157 | .TagLinkButton span[style*='rgb(134, 193, 102)']:before 158 | { content: "\f108"; } 159 | /* software */ 160 | .TagHero[style*='rgb(93, 172, 129)'] .Hero-title:before, 161 | .TagLabel[style*='rgb(93, 172, 129)'] > span:before, 162 | .TagLinkButton span[style*='rgb(93, 172, 129)']:before 163 | { content: "\f085"; } 164 | /* xyz */ 165 | .TagHero[style*='rgb(225, 107, 140)'] .Hero-title:before, 166 | .TagLabel[style*='rgb(225, 107, 140)'] > span:before, 167 | .TagLinkButton span[style*='rgb(225, 107, 140)']:before 168 | { content: "\f0a1"; } 169 | /* sandbox */ 170 | .TagHero[style*='rgb(135, 135, 135)'] .Hero-title:before, 171 | .TagLabel[style*='rgb(135, 135, 135)'] > span:before, 172 | .TagLinkButton span[style*='rgb(135, 135, 135)']:before 173 | { content: "\f0c3"; } 174 | /* contributor */ 175 | .TagHero[style*='rgb(255, 136, 153)'] .Hero-title:before, 176 | .TagLabel[style*='rgb(255, 136, 153)'] > span:before, 177 | .TagLinkButton span[style*='rgb(255, 136, 153)']:before 178 | { content: "\f4c0"; } 179 | 180 | /* xts badge */ 181 | @-webkit-keyframes fa-spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(359deg); } } 182 | @keyframes fa-spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(359deg); } } 183 | i.fa-spinner { -webkit-animation: fa-spin 1s infinite steps(8); animation: fa-spin 1s infinite steps(8); } 184 | 185 | /* mobile */ 186 | @media(max-width:1000px) { 187 | [class^=item-group], .viv-icon, .IndexPage-nav a img, .item-onlineUsers, .item-flagrow-ad { display: none !important; } 188 | li.item-session { padding-left: 12px; } 189 | #footer { width: 100%; } 190 | .item-discussion-views { right: 90px; } 191 | } 192 | -------------------------------------------------------------------------------- /flarum/core/src/Api/JsonApiResponse.php: -------------------------------------------------------------------------------- 1 | jsonSerialize(), 256), $ccconfig); 48 | opencc_close($ccconfig); 49 | parent::__construct(json_decode($cctext), $status, $headers, $encodingOptions); 50 | } else { 51 | // The call to jsonSerialize prevents rare issues with json_encode() failing with a 52 | // syntax error even though Document implements the JsonSerializable interface. 53 | // See https://github.com/flarum/core/issues/685 54 | parent::__construct($document->jsonSerialize(), $status, $headers, $encodingOptions); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /flarum/core/src/Locale/LocaleServiceProvider.php: -------------------------------------------------------------------------------- 1 | 7 | * 8 | * For the full copyright and license information, please view the LICENSE 9 | * file that was distributed with this source code. 10 | */ 11 | 12 | namespace Flarum\Locale; 13 | 14 | use Flarum\Event\ConfigureLocales; 15 | use Flarum\Foundation\AbstractServiceProvider; 16 | use Flarum\Settings\SettingsRepositoryInterface; 17 | use Illuminate\Contracts\Events\Dispatcher; 18 | use Illuminate\Contracts\Translation\Translator as TranslatorContract; 19 | use Symfony\Component\Translation\MessageSelector; 20 | use Symfony\Component\Translation\TranslatorInterface; 21 | 22 | class LocaleServiceProvider extends AbstractServiceProvider 23 | { 24 | /** 25 | * {@inheritdoc} 26 | */ 27 | public function boot(Dispatcher $events) 28 | { 29 | $locales = $this->app->make('flarum.locales'); 30 | 31 | // tofu $locales->addLocale($this->getDefaultLocale(), 'Default'); 32 | switch ($this->getDefaultLocale()) { 33 | case 'zh-hant': 34 | $locales->addLocale('zh-hant', '正體中文'); 35 | break; 36 | case 'zh-hans': 37 | $locales->addLocale('zh-hans', '简体中文'); 38 | break; 39 | default: 40 | $locales->addLocale('en', 'English'); 41 | } 42 | 43 | $events->dispatch(new ConfigureLocales($locales)); 44 | } 45 | 46 | /** 47 | * {@inheritdoc} 48 | */ 49 | public function register() 50 | { 51 | $this->app->singleton(LocaleManager::class); 52 | $this->app->alias(LocaleManager::class, 'flarum.locales'); 53 | 54 | $this->app->singleton('translator', function () { 55 | $translator = new Translator($this->getDefaultLocale(), new MessageSelector()); 56 | $translator->setFallbackLocales(['en']); 57 | $translator->addLoader('prefixed_yaml', new PrefixedYamlFileLoader()); 58 | 59 | return $translator; 60 | }); 61 | $this->app->alias('translator', Translator::class); 62 | $this->app->alias('translator', TranslatorContract::class); 63 | $this->app->alias('translator', TranslatorInterface::class); 64 | } 65 | 66 | private function getDefaultLocale(): string 67 | { 68 | $lang=""; 69 | if(isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { 70 | preg_match('/^([a-z\-]+)/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $matches); 71 | $lang = strtolower($matches[1]); 72 | } 73 | switch ($lang) { 74 | case 'zh-tw': 75 | case 'zh-hk': 76 | case 'zh': 77 | return 'zh-hant'; 78 | break; 79 | case 'zh-cn': 80 | case 'zh-sg': 81 | return 'zh-hans'; 82 | break; 83 | default: 84 | return 'en'; 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /flarum/core/views/frontend/app.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |