Browse Source

Merge remote-tracking branch 'origin/master' into notifications4

pull/4287/head
nanaya 1 month ago
parent
commit
39eacb164f
100 changed files with 622 additions and 241 deletions
  1. 1
    0
      app/Http/Controllers/Forum/TopicsController.php
  2. 1
    1
      app/Http/Controllers/HomeController.php
  3. 5
    3
      app/Http/Controllers/Store/CartController.php
  4. 3
    1
      app/Http/Controllers/Store/CheckoutController.php
  5. 5
    0
      app/Http/Controllers/Store/Controller.php
  6. 3
    1
      app/Http/Controllers/Store/NotificationRequestsController.php
  7. 7
    5
      app/Http/Controllers/StoreController.php
  8. 1
    0
      app/Http/Controllers/UsersController.php
  9. 10
    5
      app/Http/Middleware/SetLocale.php
  10. 187
    0
      app/Libraries/AcceptHttpLanguage/Parser.php
  11. 4
    0
      app/Libraries/ChangeUsername.php
  12. 25
    1
      app/Libraries/OsuAuthorize.php
  13. 11
    3
      app/Libraries/Search/BeatmapsetSearch.php
  14. 1
    1
      app/Models/BeatmapMirror.php
  15. 2
    0
      app/Models/Forum/Topic.php
  16. 10
    2
      app/Models/Forum/TopicPoll.php
  17. 9
    0
      app/Models/Model.php
  18. 7
    3
      app/Models/Store/Order.php
  19. 23
    6
      app/Models/User.php
  20. 6
    0
      app/Transformers/UserAccountHistoryTransformer.php
  21. 0
    8
      app/helpers.php
  22. 29
    0
      config/schemas/beatmaps.json
  23. 1
    0
      config/store.php
  24. 24
    24
      database/factories/StatsFactory.php
  25. 32
    0
      database/migrations/2019_04_01_202237_add_poll_hide_results_to_topics.php
  26. 1
    1
      resources/assets/coffee/_classes/tooltip-default.coffee
  27. 1
    1
      resources/assets/coffee/react/_components/comment-show-more.coffee
  28. 1
    1
      resources/assets/coffee/react/changelog-index/main.coffee
  29. 6
    2
      resources/assets/coffee/react/profile-page/account-standing.coffee
  30. 1
    1
      resources/assets/coffee/react/profile-page/beatmaps.coffee
  31. 2
    2
      resources/assets/coffee/react/profile-page/historical.coffee
  32. 1
    1
      resources/assets/coffee/react/profile-page/kudosu.coffee
  33. 2
    0
      resources/assets/coffee/react/profile-page/rank-chart.coffee
  34. 1
    1
      resources/assets/coffee/react/profile-page/recent-activity.coffee
  35. 2
    2
      resources/assets/coffee/react/profile-page/top-ranks.coffee
  36. 5
    5
      resources/assets/less/bem/beatmap-playcount.less
  37. 1
    1
      resources/assets/less/bem/builds.less
  38. 1
    1
      resources/assets/less/bem/changelog-entry.less
  39. 1
    1
      resources/assets/less/bem/game-mode-link.less
  40. 4
    4
      resources/assets/less/bem/line-chart.less
  41. 6
    6
      resources/assets/less/bem/osu-page-header-v3.less
  42. 2
    9
      resources/assets/less/bem/osu-page.less
  43. 1
    1
      resources/assets/less/bem/page-extra-tabs.less
  44. 3
    3
      resources/assets/less/bem/page-extra.less
  45. 2
    2
      resources/assets/less/bem/page-mode-link.less
  46. 11
    11
      resources/assets/less/bem/page-mode-v2.less
  47. 6
    6
      resources/assets/less/bem/play-detail.less
  48. 1
    1
      resources/assets/less/bem/post-editor.less
  49. 1
    1
      resources/assets/less/bem/profile-badges.less
  50. 1
    1
      resources/assets/less/bem/profile-detail-bar.less
  51. 1
    1
      resources/assets/less/bem/profile-detail.less
  52. 1
    1
      resources/assets/less/bem/profile-extra-entries.less
  53. 2
    2
      resources/assets/less/bem/profile-extra-recent-infringements.less
  54. 2
    2
      resources/assets/less/bem/profile-info.less
  55. 1
    1
      resources/assets/less/bem/profile-links.less
  56. 3
    3
      resources/assets/less/bem/profile-page-button.less
  57. 2
    2
      resources/assets/less/bem/profile-page-toggle.less
  58. 9
    9
      resources/assets/less/bem/show-more-link.less
  59. 1
    1
      resources/assets/less/bem/sort.less
  60. 1
    1
      resources/assets/less/bem/value-display.less
  61. 51
    18
      resources/assets/less/colors.less
  62. 1
    1
      resources/assets/less/variables.less
  63. 1
    1
      resources/lang/be/accounts.php
  64. 1
    0
      resources/lang/en/authorization.php
  65. 3
    0
      resources/lang/en/forum.php
  66. 3
    1
      resources/lang/en/model_validation.php
  67. 1
    1
      resources/lang/es/multiplayer.php
  68. 1
    1
      resources/lang/es/report.php
  69. 1
    1
      resources/lang/ja/beatmappacks.php
  70. 8
    8
      resources/lang/ja/users.php
  71. 3
    3
      resources/lang/ja/wiki.php
  72. 1
    1
      resources/lang/pl/api.php
  73. 2
    2
      resources/lang/pl/beatmaps.php
  74. 1
    1
      resources/lang/pl/common.php
  75. 2
    2
      resources/lang/pl/contest.php
  76. 1
    1
      resources/lang/pl/fulfillments.php
  77. 1
    1
      resources/lang/pl/model_validation.php
  78. 1
    1
      resources/lang/pl/model_validation/store/product.php
  79. 1
    1
      resources/lang/pl/news.php
  80. 2
    2
      resources/lang/pl/oauth.php
  81. 1
    1
      resources/lang/pl/report.php
  82. 1
    1
      resources/lang/pl/store.php
  83. 1
    1
      resources/lang/pt-br/comments.php
  84. 1
    1
      resources/lang/pt/beatmaps.php
  85. 1
    1
      resources/lang/pt/errors.php
  86. 2
    2
      resources/lang/pt/layout.php
  87. 1
    1
      resources/lang/pt/model_validation/store/product.php
  88. 10
    10
      resources/lang/pt/oauth.php
  89. 1
    1
      resources/lang/pt/store.php
  90. 1
    1
      resources/lang/ru/oauth.php
  91. 1
    1
      resources/lang/zh-tw/api.php
  92. 4
    4
      resources/lang/zh-tw/beatmaps.php
  93. 5
    5
      resources/lang/zh-tw/chat.php
  94. 2
    2
      resources/lang/zh-tw/comments.php
  95. 5
    5
      resources/lang/zh-tw/contest.php
  96. 1
    1
      resources/lang/zh-tw/errors.php
  97. 1
    1
      resources/lang/zh-tw/fulfillments.php
  98. 1
    1
      resources/lang/zh-tw/model_validation/fulfillments.php
  99. 2
    2
      resources/lang/zh-tw/model_validation/store/product.php
  100. 0
    0
      resources/lang/zh-tw/oauth.php

+ 1
- 0
app/Http/Controllers/Forum/TopicsController.php View File

@@ -445,6 +445,7 @@ class TopicsController extends Controller
445 445
     private function getPollParams()
446 446
     {
447 447
         return get_params(request(), 'forum_topic_poll', [
448
+            'hide_results:bool',
448 449
             'length_days:int',
449 450
             'max_options:int',
450 451
             'options:string_split',

+ 1
- 1
app/Http/Controllers/HomeController.php View File

@@ -139,7 +139,7 @@ class HomeController extends Controller
139 139
 
140 140
     public function setLocale()
141 141
     {
142
-        $newLocale = get_valid_locale(Request::input('locale'));
142
+        $newLocale = get_valid_locale(Request::input('locale')) ?? config('app.fallback_locale');
143 143
         App::setLocale($newLocale);
144 144
 
145 145
         if (Auth::check()) {

+ 5
- 3
app/Http/Controllers/Store/CartController.php View File

@@ -34,9 +34,11 @@ class CartController extends Controller
34 34
             'store',
35 35
         ]]);
36 36
 
37
-        $this->middleware('check-user-restricted', ['only' => [
38
-            'store',
39
-        ]]);
37
+        if (!$this->isAllowRestrictedUsers()) {
38
+            $this->middleware('check-user-restricted', ['only' => [
39
+                'store',
40
+            ]]);
41
+        }
40 42
 
41 43
         return parent::__construct();
42 44
     }

+ 3
- 1
app/Http/Controllers/Store/CheckoutController.php View File

@@ -39,7 +39,9 @@ class CheckoutController extends Controller
39 39
     public function __construct()
40 40
     {
41 41
         $this->middleware('auth');
42
-        $this->middleware('check-user-restricted');
42
+        if (!$this->isAllowRestrictedUsers()) {
43
+            $this->middleware('check-user-restricted');
44
+        }
43 45
         $this->middleware('verify-user');
44 46
 
45 47
         return parent::__construct();

+ 5
- 0
app/Http/Controllers/Store/Controller.php View File

@@ -57,4 +57,9 @@ abstract class Controller extends BaseController
57 57
             return Order::cart(Auth::user()) ?? new Order(['user_id' => Auth::user()->user_id]);
58 58
         }
59 59
     }
60
+
61
+    protected function isAllowRestrictedUsers()
62
+    {
63
+        return config('store.allow_restricted_users');
64
+    }
60 65
 }

+ 3
- 1
app/Http/Controllers/Store/NotificationRequestsController.php View File

@@ -29,7 +29,9 @@ class NotificationRequestsController extends Controller
29 29
     public function __construct()
30 30
     {
31 31
         $this->middleware('auth');
32
-        $this->middleware('check-user-restricted');
32
+        if (!$this->isAllowRestrictedUsers()) {
33
+            $this->middleware('check-user-restricted');
34
+        }
33 35
 
34 36
         return parent::__construct();
35 37
     }

+ 7
- 5
app/Http/Controllers/StoreController.php View File

@@ -42,11 +42,13 @@ class StoreController extends Controller
42 42
             'postUpdateAddress',
43 43
         ]]);
44 44
 
45
-        $this->middleware('check-user-restricted', ['only' => [
46
-            'getInvoice',
47
-            'postNewAddress',
48
-            'postUpdateAddress',
49
-        ]]);
45
+        if (!$this->isAllowRestrictedUsers()) {
46
+            $this->middleware('check-user-restricted', ['only' => [
47
+                'getInvoice',
48
+                'postNewAddress',
49
+                'postUpdateAddress',
50
+            ]]);
51
+        }
50 52
 
51 53
         $this->middleware('verify-user', ['only' => [
52 54
             'getInvoice',

+ 1
- 0
app/Http/Controllers/UsersController.php View File

@@ -268,6 +268,7 @@ class UsersController extends Controller
268 268
 
269 269
         if (priv_check('UserSilenceShowExtendedInfo')->can() && !is_api_request()) {
270 270
             $userIncludes[] = 'account_history.actor';
271
+            $userIncludes[] = 'account_history.supporting_url';
271 272
         }
272 273
 
273 274
         $userArray = json_item(

+ 10
- 5
app/Http/Middleware/SetLocale.php View File

@@ -21,8 +21,9 @@
21 21
 namespace App\Http\Middleware;
22 22
 
23 23
 use App;
24
+use App\Libraries\AcceptHttpLanguage\Parser;
24 25
 use Auth;
25
-use Carbon;
26
+use Carbon\Carbon;
26 27
 use Closure;
27 28
 use Illuminate\Http\Request;
28 29
 
@@ -41,16 +42,20 @@ class SetLocale
41 42
         if (Auth::check()) {
42 43
             $locale = Auth::user()->user_lang;
43 44
         } else {
44
-            $locale =
45
-                presence($request->cookie('locale')) ??
46
-                locale_accept_from_http($request->server('HTTP_ACCEPT_LANGUAGE'));
45
+            $locale = presence($request->cookie('locale'));
47 46
         }
48 47
 
49 48
         $locale = get_valid_locale($locale);
50 49
 
50
+        if ($locale === null) {
51
+            $accept = $request->server('HTTP_ACCEPT_LANGUAGE');
52
+            $parser = new Parser($accept);
53
+            $locale = $parser->languageRegionCompatibleFrom(config('app.available_locales')) ?? config('app.fallback_locale');
54
+        }
55
+
51 56
         App::setLocale($locale);
52 57
         // Carbon setLocale normalizes the locale
53
-        Carbon\Carbon::setLocale($locale);
58
+        Carbon::setLocale($locale);
54 59
 
55 60
         return $next($request);
56 61
     }

+ 187
- 0
app/Libraries/AcceptHttpLanguage/Parser.php View File

@@ -0,0 +1,187 @@
1
+<?php
2
+
3
+/**
4
+ *    Copyright (c) ppy Pty Ltd <contact@ppy.sh>.
5
+ *
6
+ *    This file is part of osu!web. osu!web is distributed with the hope of
7
+ *    attracting more community contributions to the core ecosystem of osu!.
8
+ *
9
+ *    osu!web is free software: you can redistribute it and/or modify
10
+ *    it under the terms of the Affero GNU General Public License version 3
11
+ *    as published by the Free Software Foundation.
12
+ *
13
+ *    osu!web is distributed WITHOUT ANY WARRANTY; without even the implied
14
+ *    warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15
+ *    See the GNU Affero General Public License for more details.
16
+ *
17
+ *    You should have received a copy of the GNU Affero General Public License
18
+ *    along with osu!web.  If not, see <http://www.gnu.org/licenses/>.
19
+ */
20
+
21
+/**
22
+ * This is a port of HttpAcceptLanguage::Parser from the http_accept_language gem
23
+ * https://github.com/iain/http_accept_language/blob/v2.1.1/lib/http_accept_language/parser.rb.
24
+ */
25
+
26
+namespace App\Libraries\AcceptHttpLanguage;
27
+
28
+use InvalidArgumentException;
29
+
30
+class Parser
31
+{
32
+    /** @var string */
33
+    public $header;
34
+
35
+    /** @var array */
36
+    private $userPreferredLanguages;
37
+
38
+    public function __construct(?string $header = null)
39
+    {
40
+        $this->header = $header;
41
+    }
42
+
43
+    /**
44
+     * Returns a sorted array based on user preference in HTTP_ACCEPT_LANGUAGE.
45
+     * Browsers send this HTTP header, so don't think this is holy.
46
+     *
47
+     * Example:
48
+     *
49
+     * request.userPreferredLanguages
50
+     * $ => [ 'nl-NL', 'nl-BE', 'nl', 'en-US', 'en' ]
51
+     */
52
+    public function userPreferredLanguages()
53
+    {
54
+        if ($this->userPreferredLanguages === null) {
55
+            try {
56
+                $this->userPreferredLanguages = $this->parseHeader();
57
+            } catch (InvalidArgumentException $_e) {
58
+                $this->userPreferredLanguages = [];
59
+            }
60
+        }
61
+
62
+        return $this->userPreferredLanguages;
63
+    }
64
+
65
+    public function setUserPreferredLanguages(array $languages)
66
+    {
67
+        $this->userPreferredLanguages = $languages;
68
+    }
69
+
70
+    /**
71
+     * Finds the locale specifically requested by the browser.
72
+     *
73
+     * Example:
74
+     *
75
+     *   request.preferred_language_from I18n.available_locales
76
+     *   $ => 'nl'
77
+     */
78
+    public function preferredLanguageFrom(array $array)
79
+    {
80
+        return array_values(array_intersect($this->userPreferredLanguages(), $array))[0] ?? null;
81
+    }
82
+
83
+    /**
84
+     * Returns the first of the userPreferredLanguages that is compatible
85
+     * with the available locales. Ignores region.
86
+     *
87
+     * Example:
88
+     *
89
+     *   request.compatibleLanguageFrom I18n.available_locales
90
+     */
91
+    public function compatibleLanguageFrom(array $availableLanguages)
92
+    {
93
+        $compatible = array_map(function ($preferred) use ($availableLanguages) { // en-US
94
+            $preferred = strtolower($preferred);
95
+            $preferredLanguage = explode('-', $preferred, 2)[0];
96
+
97
+            foreach ($availableLanguages as $available) {
98
+                if ($preferred === strtolower($available) || $preferredLanguage === explode('-', $available, 2)[0]) {
99
+                    return $available;
100
+                }
101
+            }
102
+        }, $this->userPreferredLanguages());
103
+
104
+        return array_values(array_filter($compatible))[0] ?? null; // .compact.first
105
+    }
106
+
107
+    /**
108
+     * Returns a supplied list of available locals without any extra application info
109
+     * that may be attached to the locale for storage in the application.
110
+     *
111
+     * Example:
112
+     * [ja_JP-x1, en-US-x4, en_UK-x5, fr-FR-x3] => [ja-JP, en-US, en-UK, fr-FR]
113
+     */
114
+    public function sanitizeAvailableLocales(array $availableLanguages)
115
+    {
116
+        return array_map(function ($available) {
117
+            return implode('-', array_filter(preg_split('/[_-]/', $available), function ($part) {
118
+                return !starts_with($part, 'x');
119
+            }));
120
+        }, $availableLanguages);
121
+    }
122
+
123
+    /**
124
+     * Returns the first of the user preferred languages that is
125
+     * also found in available languages.  Finds best fit by matching on
126
+     * primary language first and secondarily on region.  If no matching region is
127
+     * found, return the first language in the group matching that primary language.
128
+     *
129
+     * Example:
130
+     *
131
+     *   request.language_region_compatible($availableLanguages)
132
+     */
133
+    public function languageRegionCompatibleFrom($availableLanguages)
134
+    {
135
+        $availableLanguages = $this->sanitizeAvailableLocales($availableLanguages);
136
+        $array = array_map(function ($preferred) use ($availableLanguages) { // en-US
137
+            $preferred = strtolower($preferred);
138
+            $preferredLanguage = explode('-', $preferred, 2)[0] ?? null;
139
+
140
+            $langGroup = array_values(array_filter($availableLanguages, function ($available) use ($preferredLanguage) { // en
141
+                return $preferredLanguage === explode('-', strtolower($available), 2)[0] ?? null;
142
+            }));
143
+
144
+            foreach ($langGroup as $lang) {
145
+                if (strtolower($lang) === $preferred) {
146
+                    $result = $lang;
147
+                    break;
148
+                }
149
+            }
150
+
151
+            return $result ?? ($langGroup[0] ?? null);
152
+        }, $this->userPreferredLanguages());
153
+
154
+        return array_values(array_filter($array))[0] ?? null; // .compact.first
155
+    }
156
+
157
+    private function parseHeader()
158
+    {
159
+        $header = preg_replace('/\s+/', '', $this->header);
160
+        $header = explode(',', $header);
161
+        $mappings = array_map(function ($language) {
162
+            $exploded = explode(';q=', $language);
163
+            $locale = $exploded[0];
164
+            $quality = (float) ($exploded[1] ?? 1.0);
165
+            if (!preg_match('/^[a-z\-0-9]+|\*$/i', $locale)) {
166
+                throw new InvalidArgumentException('Not correctly formatted');
167
+            }
168
+
169
+            $locale = preg_replace_callback('/-[a-z0-9]+$/i', function ($match) {
170
+                return strtoupper($match[0]);
171
+            }, $locale); // Uppercase territory
172
+            if ($locale === '*') {
173
+                $locale = null; // Ignore wildcards
174
+            }
175
+
176
+            return [$locale, $quality];
177
+        }, $header);
178
+
179
+        usort($mappings, function ($left, $right) {
180
+            return $right[1] > $left[1];
181
+        });
182
+
183
+        return array_filter(array_map(function ($mapping) {
184
+            return $mapping[0];
185
+        }, $mappings));
186
+    }
187
+}

+ 4
- 0
app/Libraries/ChangeUsername.php View File

@@ -55,6 +55,10 @@ class ChangeUsername
55 55
             return $this->validationErrors()->addTranslated('user_id', 'This user cannot be renamed');
56 56
         }
57 57
 
58
+        if ($this->user->isRestricted()) {
59
+            return $this->validationErrors()->add('username', '.change_username.restricted');
60
+        }
61
+
58 62
         if (!$this->user->hasSupported()) {
59 63
             return $this->validationErrors()->addTranslated('username', static::requireSupportedMessage());
60 64
         }

+ 25
- 1
app/Libraries/OsuAuthorize.php View File

@@ -812,7 +812,7 @@ class OsuAuthorize
812 812
         $this->ensureLoggedIn($user);
813 813
         $this->ensureCleanRecord($user);
814 814
 
815
-        $plays = (int) $user->monthlyPlaycounts()->sum('playcount');
815
+        $plays = $user->playCount();
816 816
         $posts = $user->user_posts;
817 817
         $forInitialHelpForum = in_array($forum->forum_id, config('osu.forum.initial_help_forum_ids'), true);
818 818
 
@@ -954,6 +954,25 @@ class OsuAuthorize
954 954
         }
955 955
     }
956 956
 
957
+    public function checkForumTopicPollShowResults($user, $topic)
958
+    {
959
+        if (!$topic->poll_hide_results) {
960
+            return 'ok';
961
+        }
962
+
963
+        if ($this->doCheckUser($user, 'ForumModerate', $topic->forum)->can()) {
964
+            return 'ok';
965
+        }
966
+
967
+        if ($topic->pollEnd() === null || $topic->pollEnd()->isPast()) {
968
+            return 'ok';
969
+        }
970
+
971
+        if ($user !== null && $topic->posts()->withTrashed()->first()->poster_id === $user->user_id) {
972
+            return 'ok';
973
+        }
974
+    }
975
+
957 976
     public function checkForumTopicVote($user, $topic)
958 977
     {
959 978
         $prefix = 'forum.topic.vote.';
@@ -969,6 +988,11 @@ class OsuAuthorize
969 988
             return $prefix.'no_forum_access';
970 989
         }
971 990
 
991
+        $plays = $user->playCount();
992
+        if ($plays < config('osu.forum.minimum_plays')) {
993
+            return $prefix.'play_more';
994
+        }
995
+
972 996
         if (!$topic->poll_vote_change) {
973 997
             $userHasVoted = $topic->pollVotes()->where('vote_user_id', $user->getKey())->exists();
974 998
 

+ 11
- 3
app/Libraries/Search/BeatmapsetSearch.php View File

@@ -48,13 +48,21 @@ class BeatmapsetSearch extends RecordSearch
48 48
      */
49 49
     public function getQuery()
50 50
     {
51
+        static $partialMatchFields = ['artist', 'artist.*', 'artist_unicode', 'creator', 'title', 'title.raw', 'title.*', 'title_unicode', 'tags^0.5'];
52
+
51 53
         $query = (new BoolQuery());
52 54
 
53 55
         if (present($this->params->queryString)) {
54 56
             $terms = explode(' ', $this->params->queryString);
55
-            // results must contain at least one of the terms and boosted by containing all of them.
56
-            $query->must(QueryHelper::queryString($this->params->queryString, [], 'or', 1 / count($terms)));
57
-            $query->should(QueryHelper::queryString($this->params->queryString, [], 'and'));
57
+
58
+            // the subscoping is not necessary but prevents unintentional accidents when combining other matchers
59
+            $query->must(
60
+                (new BoolQuery)
61
+                    // results must contain at least one of the terms and boosted by containing all of them.
62
+                    ->shouldMatch(1)
63
+                    ->should(QueryHelper::queryString($this->params->queryString, $partialMatchFields, 'or', 1 / count($terms)))
64
+                    ->should(QueryHelper::queryString($this->params->queryString, [], 'and'))
65
+            );
58 66
         }
59 67
 
60 68
         $this->addModeFilter($query);

+ 1
- 1
app/Models/BeatmapMirror.php View File

@@ -63,7 +63,7 @@ class BeatmapMirror extends Model
63 63
 
64 64
     public static function getRandom()
65 65
     {
66
-        return self::randomUsable()->first();
66
+        return self::where('regions', null)->randomUsable()->first();
67 67
     }
68 68
 
69 69
     public static function getRandomFromList(array $mirrorIds)

+ 2
- 0
app/Models/Forum/Topic.php View File

@@ -49,6 +49,7 @@ use Illuminate\Database\QueryException;
49 49
  * @property int $osu_starpriority
50 50
  * @property \Illuminate\Database\Eloquent\Collection $pollOptions PollOption
51 51
  * @property \Illuminate\Database\Eloquent\Collection $pollVotes PollVote
52
+ * @property bool $poll_hide_results
52 53
  * @property int $poll_last_vote
53 54
  * @property int $poll_length
54 55
  * @property mixed $poll_length_days
@@ -126,6 +127,7 @@ class Topic extends Model implements AfterCommit
126 127
     private $_issueTags;
127 128
 
128 129
     protected $casts = [
130
+        'poll_hide_results' => 'boolean',
129 131
         'poll_vote_change' => 'boolean',
130 132
         'topic_approved' => 'boolean',
131 133
     ];

+ 10
- 2
app/Models/Forum/TopicPoll.php View File

@@ -45,7 +45,10 @@ class TopicPoll
45 45
     public function fill($params)
46 46
     {
47 47
         $this->params = array_merge([
48
+            'hide_results' => false,
49
+            'length_days' => 0,
48 50
             'max_options' => 1,
51
+            'vote_change' => false,
49 52
         ], $params);
50 53
         $this->validated = false;
51 54
 
@@ -82,6 +85,10 @@ class TopicPoll
82 85
                 $this->validationErrors()->add('max_options', '.invalid_max_options');
83 86
             }
84 87
 
88
+            if ($this->params['hide_results'] && $this->params['length_days'] === 0) {
89
+                $this->validationErrors()->add('hide_results', '.hiding_results_forever');
90
+            }
91
+
85 92
             if ($this->topic !== null && $this->topic->exists && !$this->canEdit()) {
86 93
                 $this->validationErrors()->add(
87 94
                     'edit',
@@ -104,9 +111,10 @@ class TopicPoll
104 111
             $this->topic->update([
105 112
                 'poll_title' => $this->params['title'],
106 113
                 'poll_start' => Carbon::now(),
107
-                'poll_length' => ($this->params['length_days'] ?? 0) * 3600 * 24,
114
+                'poll_length' => $this->params['length_days'] * 3600 * 24,
108 115
                 'poll_max_options' => $this->params['max_options'],
109
-                'poll_vote_change' => $this->params['vote_change'] ?? false,
116
+                'poll_vote_change' => $this->params['vote_change'],
117
+                'poll_hide_results' => $this->params['hide_results'],
110 118
             ]);
111 119
 
112 120
             $this

+ 9
- 0
app/Models/Model.php View File

@@ -74,6 +74,15 @@ abstract class Model extends BaseModel
74 74
         };
75 75
     }
76 76
 
77
+    public function refresh()
78
+    {
79
+        if (isset($this->memoized)) {
80
+            $this->memoized = [];
81
+        }
82
+
83
+        return parent::refresh();
84
+    }
85
+
77 86
     public function scopeCursorWhere($query, array $cursors, bool $isFirst = true)
78 87
     {
79 88
         if (empty($cursors)) {

+ 7
- 3
app/Models/Store/Order.php View File

@@ -558,9 +558,13 @@ class Order extends Model
558 558
         // FIXME: custom class stuff should probably not go in Order...
559 559
         switch ($product->custom_class) {
560 560
             case 'supporter-tag':
561
-                $targetId = $params['extraData']['target_id'];
562
-                $user = User::default()->where('user_id', $targetId)->firstOrFail();
563
-                $params['extraData']['username'] = $user->username;
561
+                $targetId = (int) $params['extraData']['target_id'];
562
+                if ($targetId === $this->user_id) {
563
+                    $params['extraData']['username'] = $this->user->username;
564
+                } else {
565
+                    $user = User::default()->where('user_id', $targetId)->firstOrFail();
566
+                    $params['extraData']['username'] = $user->username;
567
+                }
564 568
 
565 569
                 $params['extraData']['duration'] = SupporterTag::getDuration($params['cost']);
566 570
                 break;

+ 23
- 6
app/Models/User.php View File

@@ -221,7 +221,7 @@ class User extends Model implements AuthenticatableContract
221 221
         'user_interests' => 30,
222 222
     ];
223 223
 
224
-    private $memoized = [];
224
+    protected $memoized = [];
225 225
 
226 226
     private $validateCurrentPassword = false;
227 227
     private $validatePasswordConfirmation = false;
@@ -389,13 +389,9 @@ class User extends Model implements AuthenticatableContract
389 389
             return Carbon::now()->addYears(10);
390 390
         }
391 391
 
392
-        $playCount = array_reduce(array_keys(Beatmap::MODES), function ($result, $mode) {
393
-            return $result + $this->statistics($mode, true)->value('playcount');
394
-        }, 0);
395
-
396 392
         return $this->user_lastvisit
397 393
             ->addDays(static::INACTIVE_DAYS) //base inactivity period for all accounts
398
-            ->addDays($playCount * 0.75);    //bonus based on playcount
394
+            ->addDays($this->playCount() * 0.75);    //bonus based on playcount
399 395
     }
400 396
 
401 397
     public function validateChangeUsername(string $username)
@@ -1559,6 +1555,27 @@ class User extends Model implements AuthenticatableContract
1559 1555
         return static::attemptLogin($this, $password) === null;
1560 1556
     }
1561 1557
 
1558
+    public function playCount()
1559
+    {
1560
+        if (!array_key_exists(__FUNCTION__, $this->memoized)) {
1561
+            $unionQuery = null;
1562
+
1563
+            foreach (Beatmap::MODES as $key => $_value) {
1564
+                $query = $this->statistics($key, true)->select('playcount');
1565
+
1566
+                if ($unionQuery === null) {
1567
+                    $unionQuery = $query;
1568
+                } else {
1569
+                    $unionQuery->unionAll($query);
1570
+                }
1571
+            }
1572
+
1573
+            $this->memoized[__FUNCTION__] = $unionQuery->get()->sum('playcount');
1574
+        }
1575
+
1576
+        return $this->memoized[__FUNCTION__];
1577
+    }
1578
+
1562 1579
     public function profileCustomization()
1563 1580
     {
1564 1581
         if (!array_key_exists(__FUNCTION__, $this->memoized)) {

+ 6
- 0
app/Transformers/UserAccountHistoryTransformer.php View File

@@ -27,6 +27,7 @@ class UserAccountHistoryTransformer extends Fractal\TransformerAbstract
27 27
 {
28 28
     protected $availableIncludes = [
29 29
         'actor',
30
+        'supporting_url',
30 31
     ];
31 32
 
32 33
     public function transform(UserAccountHistory $h)
@@ -45,4 +46,9 @@ class UserAccountHistoryTransformer extends Fractal\TransformerAbstract
45 46
             return $this->item($h->actor, new UserCompactTransformer);
46 47
         }
47 48
     }
49
+
50
+    public function includeSupportingUrl(UserAccountHistory $h)
51
+    {
52
+        return $this->primitive($h->supporting_url);
53
+    }
48 54
 }

+ 0
- 8
app/helpers.php View File

@@ -205,14 +205,6 @@ function get_valid_locale($requestedLocale)
205 205
     if (in_array($requestedLocale, config('app.available_locales'), true)) {
206 206
         return $requestedLocale;
207 207
     }
208
-
209
-    return array_first(
210
-        config('app.available_locales'),
211
-        function ($value) use ($requestedLocale) {
212
-            return starts_with($requestedLocale, $value);
213
-        },
214
-        config('app.fallback_locale')
215
-    );
216 208
 }
217 209
 
218 210
 function html_entity_decode_better($string)

+ 29
- 0
config/schemas/beatmaps.json View File

@@ -160,6 +160,11 @@
160 160
         "title": {
161 161
           "type": "text",
162 162
           "fields": {
163
+            "_prefix": {
164
+              "type": "text",
165
+              "analyzer": "prefix",
166
+              "search_analyzer": "standard"
167
+            },
163 168
             "raw": {
164 169
               "type": "keyword"
165 170
             }
@@ -179,6 +184,30 @@
179 184
   },
180 185
   "settings": {
181 186
     "index": {
187
+      "analysis": {
188
+        "analyzer": {
189
+          "lowercase": {
190
+            "tokenizer": "lowercase"
191
+          },
192
+          "prefix": {
193
+            "filter": [
194
+              "lowercase"
195
+            ],
196
+            "tokenizer": "prefix"
197
+          }
198
+        },
199
+        "tokenizer": {
200
+          "prefix": {
201
+            "type": "edge_ngram",
202
+            "min_gram": "3",
203
+            "max_gram": "8",
204
+            "token_chars": [
205
+              "letter",
206
+              "digit"
207
+            ]
208
+          }
209
+        }
210
+      },
182 211
       "number_of_shards": "1",
183 212
       "number_of_replicas": "1"
184 213
     }

+ 1
- 0
config/store.php View File

@@ -1,6 +1,7 @@
1 1
 <?php
2 2
 
3 3
 return [
4
+    'allow_restricted_users' => get_bool(env('STORE_ALLOW_RESTRICTED_USERS')) ?? false,
4 5
     'invoice' => [
5 6
         'max_copies' => get_int(env('STORE_INVOICE_MAX_COPIES')) ?? 10,
6 7
     ],

+ 24
- 24
database/factories/StatsFactory.php View File

@@ -11,29 +11,30 @@ if (!function_exists('generateStats')) {
11 11
         $playcount = rand(1000, 250000); // 1k - 250k
12 12
 
13 13
         return [
14
-          'level' => rand(1, 104),
15
-          'count300' => rand(10000, 5000000), // 10k to 5mil
16
-          'count100' => rand(10000, 2000000), // 10k to 2mil
17
-          'count50' => rand(10000, 1000000), // 10k to 1mil
18
-          'countMiss' => rand(10000, 1000000), // 10k to 1mil
19
-          'accuracy_total' => rand(1000, 250000), // 1k to 250k. unsure what field is for
20
-          'accuracy_count' => rand(1000, 250000), // 1k to 250k. unsure what field is for
21
-          'accuracy' => $acc / 100,
22
-          'accuracy_new' => $acc,
23
-          'playcount' => $playcount,
24
-          'fail_count' => rand($playcount * 0.1, $playcount * 0.2),
25
-          'exit_count' => rand($playcount * 0.2, $playcount * 0.3),
26
-          'ranked_score' => $score,
27
-          'total_score' => $score * 1.4,
28
-          'total_seconds_played' => rand($playcount * 120 * 0.3, $playcount * 120 * 0.7),
29
-          'x_rank_count' => round($playcount * 0.001),
30
-          'xh_rank_count' => round($playcount * 0.0003),
31
-          's_rank_count' => round($playcount * 0.05),
32
-          'sh_rank_count' => round($playcount * 0.02),
33
-          'a_rank_count' => round($playcount * 0.2),
34
-          'rank_score' => $pp,
35
-          'rank_score_index' => rand(1, 500000),
36
-          'max_combo' => rand(500, 4000),
14
+            'level' => rand(1, 104),
15
+            'count300' => rand(10000, 5000000), // 10k to 5mil
16
+            'count100' => rand(10000, 2000000), // 10k to 2mil
17
+            'count50' => rand(10000, 1000000), // 10k to 1mil
18
+            'countMiss' => rand(10000, 1000000), // 10k to 1mil
19
+            'accuracy_total' => rand(1000, 250000), // 1k to 250k. unsure what field is for
20
+            'accuracy_count' => rand(1000, 250000), // 1k to 250k. unsure what field is for
21
+            'accuracy' => $acc / 100,
22
+            'accuracy_new' => $acc,
23
+            'playcount' => $playcount,
24
+            'fail_count' => rand($playcount * 0.1, $playcount * 0.2),
25
+            'exit_count' => rand($playcount * 0.2, $playcount * 0.3),
26
+            'rank' => rand(1, 500000),
27
+            'ranked_score' => $score,
28
+            'total_score' => $score * 1.4,
29
+            'total_seconds_played' => rand($playcount * 120 * 0.3, $playcount * 120 * 0.7),
30
+            'x_rank_count' => round($playcount * 0.001),
31
+            'xh_rank_count' => round($playcount * 0.0003),
32
+            's_rank_count' => round($playcount * 0.05),
33
+            'sh_rank_count' => round($playcount * 0.02),
34
+            'a_rank_count' => round($playcount * 0.2),
35
+            'rank_score' => $pp,
36
+            'rank_score_index' => rand(1, 500000),
37
+            'max_combo' => rand(500, 4000),
37 38
         ];
38 39
     }
39 40
 }
@@ -45,7 +46,6 @@ foreach (array_keys(App\Models\Beatmap::MODES) as $mode) {
45 46
 
46 47
     $factory->define(App\Models\UserStatistics\Spotlight\Model::getClass($mode), function (Faker\Generator $faker) {
47 48
         $stats = generateStats();
48
-        $stats['rank'] = rand(1, 500000);
49 49
         unset($stats['accuracy_new']);
50 50
         unset($stats['total_seconds_played']);
51 51
         unset($stats['xh_rank_count']);

+ 32
- 0
database/migrations/2019_04_01_202237_add_poll_hide_results_to_topics.php View File

@@ -0,0 +1,32 @@
1
+<?php
2
+
3
+use Illuminate\Database\Migrations\Migration;
4
+use Illuminate\Database\Schema\Blueprint;
5
+use Illuminate\Support\Facades\Schema;
6
+
7
+class AddPollHideResultsToTopics extends Migration
8
+{
9
+    /**
10
+     * Run the migrations.
11
+     *
12
+     * @return void
13
+     */
14
+    public function up()
15
+    {
16
+        Schema::table('phpbb_topics', function (Blueprint $table) {
17
+            $table->boolean('poll_hide_results')->after('poll_vote_change')->default(0);
18
+        });
19
+    }
20
+
21
+    /**
22
+     * Reverse the migrations.
23
+     *
24
+     * @return void
25
+     */
26
+    public function down()
27
+    {
28
+        Schema::table('phpbb_topics', function (Blueprint $table) {
29
+            $table->dropColumn('poll_hide_results');
30
+        });
31
+    }
32
+}

+ 1
- 1
resources/assets/coffee/_classes/tooltip-default.coffee View File

@@ -133,7 +133,7 @@ class @TooltipDefault
133 133
       .text time.format('LL')
134 134
     $timeEl = $('<span>')
135 135
       .addClass 'tooltip-default__time'
136
-      .text "#{time.format('LT')} #{@tzString(time)}"
136
+      .text "#{time.format('LTS')} #{@tzString(time)}"
137 137
 
138 138
     $('<span>')
139 139
       .append $dateEl

+ 1
- 1
resources/assets/coffee/react/_components/comment-show-more.coffee View File

@@ -47,7 +47,7 @@ class @CommentShowMore extends React.PureComponent
47 47
       remaining = @props.total - @props.comments.length
48 48
       modifiers = ['comments']
49 49
       if 'changelog' in @props.modifiers
50
-        modifiers.push('t-dark-purple-darker')
50
+        modifiers.push('t-greyviolet-darker')
51 51
       else
52 52
         modifiers.push('t-ddd')
53 53
 

+ 1
- 1
resources/assets/coffee/react/changelog-index/main.coffee View File

@@ -69,7 +69,7 @@ class ChangelogIndex.Main extends React.PureComponent
69 69
           callback: @showMore
70 70
           hasMore: @state.hasMore
71 71
           loading: @state.loading
72
-          modifiers: ['t-dark-purple-darker', 'changelog-index']
72
+          modifiers: ['t-greyviolet-darker', 'changelog-index']
73 73
 
74 74
 
75 75
   renderHeaderTabs: =>

+ 6
- 2
resources/assets/coffee/react/profile-page/account-standing.coffee View File

@@ -15,7 +15,7 @@
15 15
 #    You should have received a copy of the GNU Affero General Public License
16 16
 #    along with osu!web.  If not, see <http://www.gnu.org/licenses/>.
17 17
 ###
18
-{div, span, h3, table, thead, tbody, tr, th, td, time} = ReactDOMFactories
18
+{a, div, span, h3, table, thead, tbody, tr, th, td, time} = ReactDOMFactories
19 19
 el = React.createElement
20 20
 
21 21
 bn = 'profile-extra-recent-infringements'
@@ -100,7 +100,11 @@ class ProfilePage.AccountStanding extends React.PureComponent
100 100
           className: "#{bn}__table-cell #{bn}__table-cell--description"
101 101
           span
102 102
             className: "#{bn}__description"
103
-            event.description
103
+            if currentUser.is_admin && event.supporting_url?
104
+              a href: event.supporting_url, event.description
105
+            else
106
+              event.description
107
+
104 108
             if currentUser.is_admin && event.actor?
105 109
               span
106 110
                 className: "#{bn}__actor"

+ 1
- 1
resources/assets/coffee/react/profile-page/beatmaps.coffee View File

@@ -62,7 +62,7 @@ class ProfilePage.Beatmaps extends React.PureComponent
62 62
           div
63 63
             className: 'osu-layout__col',
64 64
             el ShowMoreLink,
65
-              modifiers: ['profile-page', 't-community-user-graygreen-darker']
65
+              modifiers: ['profile-page', 't-greyseafoam-dark']
66 66
               event: 'profile:showMore'
67 67
               hasMore: @props.pagination[section].hasMore
68 68
               loading: @props.pagination[section].loading

+ 2
- 2
resources/assets/coffee/react/profile-page/historical.coffee View File

@@ -75,7 +75,7 @@ class ProfilePage.Historical extends React.PureComponent
75 75
               playcount: playcount
76 76
           el ShowMoreLink,
77 77
             key: 'show-more-row'
78
-            modifiers: ['profile-page', 't-community-user-graygreen-darker']
78
+            modifiers: ['profile-page', 't-greyseafoam-dark']
79 79
             event: 'profile:showMore'
80 80
             hasMore: @props.pagination.beatmapPlaycounts.hasMore
81 81
             loading: @props.pagination.beatmapPlaycounts.loading
@@ -99,7 +99,7 @@ class ProfilePage.Historical extends React.PureComponent
99 99
 
100 100
           el ShowMoreLink,
101 101
             key: 'show-more-row'
102
-            modifiers: ['profile-page', 't-community-user-graygreen-darker']
102
+            modifiers: ['profile-page', 't-greyseafoam-dark']
103 103
             event: 'profile:showMore'
104 104
             hasMore: @props.pagination.scoresRecent.hasMore
105 105
             loading: @props.pagination.scoresRecent.loading

+ 1
- 1
resources/assets/coffee/react/profile-page/kudosu.coffee View File

@@ -67,7 +67,7 @@ class ProfilePage.Kudosu extends React.Component
67 67
 
68 68
           li className: 'profile-extra-entries__item',
69 69
             el ShowMoreLink,
70
-              modifiers: ['profile-page', 't-community-user-graygreen-darker']
70
+              modifiers: ['profile-page', 't-greyseafoam-dark']
71 71
               event: 'profile:showMore'
72 72
               hasMore: @props.pagination.recentlyReceivedKudosu.hasMore
73 73
               loading: @props.pagination.recentlyReceivedKudosu.loading

+ 2
- 0
resources/assets/coffee/react/profile-page/rank-chart.coffee View File

@@ -85,6 +85,8 @@ class ProfilePage.RankChart extends React.Component
85 85
       y: -rank
86 86
     .filter (point) -> point.y < 0
87 87
 
88
+    return unless data.length > 0
89
+
88 90
     if data.length == 1
89 91
       data.unshift
90 92
         x: data[0].x - 1

+ 1
- 1
resources/assets/coffee/react/profile-page/recent-activity.coffee View File

@@ -37,7 +37,7 @@ class ProfilePage.RecentActivity extends React.PureComponent
37 37
           div
38 38
             className: 'profile-extra-entries__item'
39 39
             el ShowMoreLink,
40
-              modifiers: ['profile-page', 't-community-user-graygreen-darker']
40
+              modifiers: ['profile-page', 't-greyseafoam-dark']
41 41
               event: 'profile:showMore'
42 42
               hasMore: @props.pagination.recentActivity.hasMore
43 43
               loading: @props.pagination.recentActivity.loading

+ 2
- 2
resources/assets/coffee/react/profile-page/top-ranks.coffee View File

@@ -33,7 +33,7 @@ class ProfilePage.TopRanks extends React.PureComponent
33 33
 
34 34
             div className: 'profile-extra-entries__item',
35 35
               el ShowMoreLink,
36
-                modifiers: ['profile-page', 't-community-user-graygreen-darker']
36
+                modifiers: ['profile-page', 't-greyseafoam-dark']
37 37
                 event: 'profile:showMore'
38 38
                 hasMore: @props.pagination.scoresBest.hasMore
39 39
                 loading: @props.pagination.scoresBest.loading
@@ -60,7 +60,7 @@ class ProfilePage.TopRanks extends React.PureComponent
60 60
 
61 61
             div className: 'profile-extra-entries__item',
62 62
               el ShowMoreLink,
63
-                modifiers: ['profile-page', 't-community-user-graygreen-darker']
63
+                modifiers: ['profile-page', 't-greyseafoam-dark']
64 64
                 event: 'profile:showMore'
65 65
                 hasMore: @props.pagination.scoresFirsts.hasMore
66 66
                 loading: @props.pagination.scoresFirsts.loading

+ 5
- 5
resources/assets/less/bem/beatmap-playcount.less View File

@@ -80,13 +80,13 @@
80 80
     flex: 1;
81 81
     min-width: 0;
82 82
     display: flex;
83
-    background-color: @community-user-graygreen-darker;
83
+    background-color: @greyseafoam-dark;
84 84
     border-radius: @border-radius-large;
85 85
     margin-left: -@border-radius-large;
86 86
     padding: 10px;
87 87
 
88 88
     .@{_top}:hover & {
89
-      background-color: @community-user-graygreen-dark;
89
+      background-color: @greyseafoam;
90 90
     }
91 91
   }
92 92
 
@@ -109,15 +109,15 @@
109 109
   }
110 110
 
111 111
   &__mapper {
112
-    color: @community-user-graygreen-lighter;
112
+    color: @greyseafoam-lighter;
113 113
   }
114 114
 
115 115
   &__mapper-link {
116 116
     font-weight: bold;
117
-    color: @community-user-graygreen-lighter;
117
+    color: @greyseafoam-lighter;
118 118
 
119 119
     .link-hover({
120
-      color: @community-user-graygreen-lighter;
120
+      color: @greyseafoam-lighter;
121 121
     });
122 122
   }
123 123
 

+ 1
- 1
resources/assets/less/bem/builds.less View File

@@ -46,7 +46,7 @@
46 46
     padding: 0 0 30px;
47 47
 
48 48
     & + & {
49
-      border-top: 1px solid @dark-purple-darker;
49
+      border-top: 1px solid @greyviolet-darker;
50 50
     }
51 51
   }
52 52
 }

+ 1
- 1
resources/assets/less/bem/changelog-entry.less View File

@@ -53,7 +53,7 @@
53 53
     // having anything but markdown-formatted page inside may result in sadness.
54 54
     &--message {
55 55
       font-size: @font-size--normal;
56
-      color: @dark-purple-lighter;
56
+      color: @greyviolet-lighter;
57 57
       margin-top: 2px;
58 58
 
59 59
       h1, h2, h3, h4, h5, h6 {

+ 1
- 1
resources/assets/less/bem/game-mode-link.less View File

@@ -20,7 +20,7 @@
20 20
   .default-text-shadow();
21 21
   .link-plain();
22 22
   font-size: @font-size--title-small-4;
23
-  color: @community-user-green;
23
+  color: @seafoam;
24 24
   padding: 0 10px;
25 25
 
26 26
   .link-hover({

+ 4
- 4
resources/assets/less/bem/line-chart.less View File

@@ -46,7 +46,7 @@
46 46
 
47 47
     .@{_top}--profile-page & {
48 48
       .circle(@_marker-radius-profile-page * 2);
49
-      background-color: @community-user-graygreen-darkest;
49
+      background-color: @greyseafoam-darker;
50 50
       border-color: @yellow;
51 51
       border-width: (@_line-width-profile-page * 2);
52 52
       top: (-@_marker-radius-profile-page);
@@ -74,7 +74,7 @@
74 74
     left: 0;
75 75
     top: 0;
76 76
     margin: 5px;
77
-    background-color: @community-user-graygreen-darker;
77
+    background-color: @greyseafoam-dark;
78 78
     padding: 10px;
79 79
     border-radius: 10px;
80 80
     font-size: @font-size--normal;
@@ -96,7 +96,7 @@
96 96
 
97 97
     &--x {
98 98
       .@{_top}--profile-page & {
99
-        color: @community-user-graygreen-lighter;
99
+        color: @greyseafoam-lighter;
100 100
         order: 1;
101 101
       }
102 102
       .@{_top}--status-page & {
@@ -158,7 +158,7 @@
158 158
     }
159 159
 
160 160
     .@{_top}--profile-page & {
161
-      fill: @community-user-graygreen-lighter;
161
+      fill: @greyseafoam-lighter;
162 162
     }
163 163
   }
164 164
 }

+ 6
- 6
resources/assets/less/bem/osu-page-header-v3.less View File

@@ -39,7 +39,7 @@
39 39
     background-repeat: no-repeat;
40 40
 
41 41
     .@{_top}--changelog & {
42
-      box-shadow: inset 0 0 0 2px @dark-purple;
42
+      box-shadow: inset 0 0 0 2px @violet;
43 43
       background-image: url('/images/icons/changelog.svg');
44 44
     }
45 45
 
@@ -49,12 +49,12 @@
49 49
     }
50 50
 
51 51
     .@{_top}--news & {
52
-      box-shadow: inset 0 0 0 2px @dark-purple;
52
+      box-shadow: inset 0 0 0 2px @violet;
53 53
       background-image: url('/images/icons/news.svg');
54 54
     }
55 55
 
56 56
     .@{_top}--users & {
57
-      box-shadow: inset 0 0 0 2px @community-user-green;
57
+      box-shadow: inset 0 0 0 2px @seafoam;
58 58
       background-image: url('/images/icons/profile.svg');
59 59
     }
60 60
   }
@@ -72,7 +72,7 @@
72 72
 
73 73
   &__title-highlight {
74 74
     .@{_top}--changelog & {
75
-      color: @dark-purple;
75
+      color: @violet;
76 76
     }
77 77
 
78 78
     .@{_top}--chat & {
@@ -84,11 +84,11 @@
84 84
     }
85 85
 
86 86
     .@{_top}--news & {
87
-      color: @dark-purple;
87
+      color: @violet;
88 88
     }
89 89
 
90 90
     .@{_top}--users & {
91
-      color: @community-user-green;
91
+      color: @seafoam;
92 92
     }
93 93
   }
94 94
 

+ 2
- 9
resources/assets/less/bem/osu-page.less View File

@@ -158,7 +158,7 @@
158 158
     .default-gutter();
159 159
     padding-bottom: 20px;
160 160
     padding-top: 20px;
161
-    background-color: @dark-purple-darker;
161
+    background-color: @greyviolet-darker;
162 162
     color: #fff;
163 163
     margin-bottom: 10px;
164 164
   }
@@ -189,13 +189,6 @@
189 189
   }
190 190
 
191 191
   &--small {
192
-    .width(20px);
193
-    @media @desktop {
194
-      .width-desktop(20px);
195
-    }
196
-  }
197
-
198
-  &--small-desktop {
199 192
     @media @desktop {
200 193
       .width-desktop(20px);
201 194
     }
@@ -215,7 +208,7 @@
215 208
 
216 209
   &--users {
217 210
     .default-box-shadow();
218
-    background-color: @community-user-graygreen-darker;
211
+    background-color: @greyseafoam-dark;
219 212
     color: #fff;
220 213
     margin-bottom: 10px;
221 214
   }

+ 1
- 1
resources/assets/less/bem/page-extra-tabs.less View File

@@ -32,6 +32,6 @@
32 32
   &--profile-page {
33 33
     background-color: #111;
34 34
     border-bottom-width: 2px;
35
-    border-bottom-color: @community-user-green;
35
+    border-bottom-color: @seafoam;
36 36
   }
37 37
 }

+ 3
- 3
resources/assets/less/bem/page-extra.less View File

@@ -22,7 +22,7 @@
22 22
   .own-layer();
23 23
   .default-box-shadow();
24 24
   .at2x-simple('/images/backgrounds/page-extra-footer.png');
25
-  background-color: @community-user-graygreen-darkest;
25
+  background-color: @greyseafoam-darker;
26 26
   background-position: bottom center;
27 27
   background-size: contain;
28 28
   background-repeat: no-repeat;
@@ -116,7 +116,7 @@
116 116
   }
117 117
 
118 118
   &__recent-medals-box {
119
-    background-color: @community-user-graygreen-darker;
119
+    background-color: @greyseafoam-dark;
120 120
     margin: 0 (-@gutter-profile-page);
121 121
     padding: 0 @gutter-profile-page 20px;
122 122
 
@@ -131,7 +131,7 @@
131 131
   &__title {
132 132
     display: inline-block;
133 133
     color: #fff;
134
-    border-bottom: 2px solid @community-user-green;
134
+    border-bottom: 2px solid @seafoam;
135 135
     padding-bottom: 5px;
136 136
     margin: 0 0 20px;
137 137
     font-style: normal;

+ 2
- 2
resources/assets/less/bem/page-mode-link.less View File

@@ -64,7 +64,7 @@
64 64
   }
65 65
 
66 66
   &--profile-page {
67
-    color: @community-user-green;
67
+    color: @seafoam;
68 68
     font-size: @font-size--title-small-3;
69 69
     padding-top: 10px;
70 70
 
@@ -107,7 +107,7 @@
107 107
     }
108 108
 
109 109
     .@{top}--profile-page & {
110
-      background-color: @community-user-green;
110
+      background-color: @seafoam;
111 111
       height: 10px;
112 112
       bottom: -6px;
113 113
       border-radius: 10000px;

+ 11
- 11
resources/assets/less/bem/page-mode-v2.less View File

@@ -47,7 +47,7 @@
47 47
 
48 48
   &--changelog {
49 49
     &::after {
50
-      background-color: @dark-purple;
50
+      background-color: @violet;
51 51
     }
52 52
   }
53 53
 
@@ -65,13 +65,13 @@
65 65
 
66 66
   &--news {
67 67
     &::after {
68
-      background-color: @dark-purple;
68
+      background-color: @violet;
69 69
     }
70 70
   }
71 71
 
72 72
   &--users {
73 73
     &::after {
74
-      background-color: @community-user-green;
74
+      background-color: @seafoam;
75 75
     }
76 76
   }
77 77
 
@@ -88,7 +88,7 @@
88 88
       margin: 0 5px;
89 89
 
90 90
       .@{_top}--changelog & {
91
-        color: @dark-purple;
91
+        color: @violet;
92 92
       }
93 93
 
94 94
       .@{_top}--chat & {
@@ -100,7 +100,7 @@
100 100
       }
101 101
 
102 102
       .@{_top}--news & {
103
-        color: @dark-purple;
103
+        color: @violet;
104 104
       }
105 105
 
106 106
       .@{_top}--users & {
@@ -140,10 +140,10 @@
140 140
     }
141 141
 
142 142
     .@{_top}--changelog & {
143
-      color: @dark-purple;
143
+      color: @violet;
144 144
 
145 145
       &::before {
146
-        background-color: @dark-purple;
146
+        background-color: @violet;
147 147
       }
148 148
     }
149 149
 
@@ -164,18 +164,18 @@
164 164
     }
165 165
 
166 166
     .@{_top}--news & {
167
-      color: @dark-purple;
167
+      color: @violet;
168 168
 
169 169
       &::before {
170
-        background-color: @dark-purple;
170
+        background-color: @violet;
171 171
       }
172 172
     }
173 173
 
174 174
     .@{_top}--users & {
175
-      color: @community-user-green;
175
+      color: @seafoam;
176 176
 
177 177
       &::before {
178
-        background-color: @community-user-green;
178
+        background-color: @seafoam;
179 179
       }
180 180
     }
181 181
 

+ 6
- 6
resources/assets/less/bem/play-detail.less View File

@@ -24,13 +24,13 @@
24 24
   @_padding-horizontal: 20px;
25 25
   @_menu-button-width: (@_padding-horizontal * 2);
26 26
   @_border-radius: 10px;
27
-  @_bg-main: @community-user-graygreen-dark;
28
-  @_bg-main-hover: @community-user-graygreen;
29
-  @_bg-extra: @community-user-graygreen-darker;
30
-  @_bg-extra-hover: @community-user-graygreen-dark;
27
+  @_bg-main: @greyseafoam;
28
+  @_bg-main-hover: @greyseafoam-light;
29
+  @_bg-extra: @greyseafoam-dark;
30
+  @_bg-extra-hover: @greyseafoam;
31 31
 
32 32
   border-radius: @_border-radius;
33
-  background-color: @community-user-graygreen-darker;
33
+  background-color: @greyseafoam-dark;
34 34
   line-height: normal;
35 35
   color: #fff;
36 36
   font-size: @font-size--normal;
@@ -207,7 +207,7 @@
207 207
     flex: none;
208 208
     font-size: @font-size--title-small-3;
209 209
     font-weight: 700;
210
-    color: @community-user-green-light;
210
+    color: @greylime-lighter;
211 211
 
212 212
     @media @desktop {
213 213
       .center-content();

+ 1
- 1
resources/assets/less/bem/post-editor.less View File

@@ -44,7 +44,7 @@
44 44
     }
45 45
 
46 46
     .@{_top}--profile-page & {
47
-      background-color: @community-user-graygreen-darker;
47
+      background-color: @greyseafoam-dark;
48 48
       padding: 10px @gutter-profile-page;
49 49
 
50 50
       @media @desktop {

+ 1
- 1
resources/assets/less/bem/profile-badges.less View File

@@ -25,7 +25,7 @@
25 25
   padding: (@padding-profile-page - @_padding) (@gutter-profile-page - @_padding);
26 26
   display: flex;
27 27
   flex-wrap: wrap;
28
-  background-color: @community-user-graygreen-darkest;
28
+  background-color: @greyseafoam-darker;
29 29
 
30 30
   @media @desktop {
31 31
     padding-left: (@gutter-profile-page-desktop - @_padding);

+ 1
- 1
resources/assets/less/bem/profile-detail-bar.less View File

@@ -26,7 +26,7 @@
26 26
   padding: (@padding-profile-page - @_column-padding) (@gutter-profile-page - @_column-padding);
27 27
   justify-content: space-between;
28 28
   align-items: center;
29
-  background-color: @community-user-graygreen-dark;
29
+  background-color: @greyseafoam;
30 30
 
31 31
   @media @desktop {
32 32
     height: 60px;

+ 1
- 1
resources/assets/less/bem/profile-detail.less View File

@@ -18,7 +18,7 @@
18 18
 
19 19
 .profile-detail {
20 20
   .inner-shadow-top();
21
-  background-color: @community-user-graygreen-darkest;
21
+  background-color: @greyseafoam-darker;
22 22
 
23 23
   &__col {
24 24
     flex: none;

+ 1
- 1
resources/assets/less/bem/profile-extra-entries.less View File

@@ -134,6 +134,6 @@
134 134
   }
135 135
 
136 136
   &__time {
137
-    color: @community-user-graygreen-lighter;
137
+    color: @greyseafoam-lighter;
138 138
   }
139 139
 }

+ 2
- 2
resources/assets/less/bem/profile-extra-recent-infringements.less View File

@@ -65,7 +65,7 @@
65 65
     }
66 66
 
67 67
     &--date {
68
-      color: @community-user-graygreen-lighter;
68
+      color: @greyseafoam-lighter;
69 69
       width: 160px;
70 70
       padding-right: 20px;
71 71
     }
@@ -85,7 +85,7 @@
85 85
   }
86 86
 
87 87
   &__actor {
88
-    color: @community-user-graygreen-lighter;
88
+    color: @greyseafoam-lighter;
89 89
 
90 90
     &::before {
91 91
       content: ' ';

+ 2
- 2
resources/assets/less/bem/profile-info.less View File

@@ -74,9 +74,9 @@
74 74
     align-items: center;
75 75
     font-size: @font-size--title-small;
76 76
 
77
-    color: @community-user-graygreen-lighter;
77
+    color: @greyseafoam-lighter;
78 78
     .link-hover({
79
-      color: @community-user-graygreen-lighter;
79
+      color: @greyseafoam-lighter;
80 80
     });
81 81
 
82 82
     @media @desktop {

+ 1
- 1
resources/assets/less/bem/profile-links.less View File

@@ -27,7 +27,7 @@
27 27
   }
28 28
 
29 29
   &__icon {
30
-    color: @community-user-graygreen-lighter;
30
+    color: @greyseafoam-lighter;
31 31
     margin-right: 2px;
32 32
   }
33 33
 

+ 3
- 3
resources/assets/less/bem/profile-page-button.less View File

@@ -20,19 +20,19 @@
20 20
   .reset-input();
21 21
   font-size: @font-size--normal;
22 22
   border-radius: 10000px;
23
-  background-color: @community-user-graygreen-darker;
23
+  background-color: @greyseafoam-dark;
24 24
   color: #fff;
25 25
   padding: 5px 20px;
26 26
 
27 27
   &:hover {
28
-    background-color: @community-user-graygreen-dark;
28
+    background-color: @greyseafoam;
29 29
   }
30 30
 
31 31
   &[disabled] {
32 32
     color: #ccc;
33 33
 
34 34
     &:hover {
35
-      background-color: @community-user-graygreen-darker;
35
+      background-color: @greyseafoam-dark;
36 36
     }
37 37
   }
38 38
 }

+ 2
- 2
resources/assets/less/bem/profile-page-toggle.less View File

@@ -19,9 +19,9 @@
19 19
 .profile-page-toggle {
20 20
   .reset-input();
21 21
   .circle(40px);
22
-  background-color: @community-user-graygreen;
22
+  background-color: @greyseafoam-light;
23 23
 
24 24
   .link-hover({
25
-    background-color: darken(@community-user-graygreen, 5%);
25
+    background-color: darken(@greyseafoam-light, 5%);
26 26
   });
27 27
 }

+ 9
- 9
resources/assets/less/bem/show-more-link.less View File

@@ -54,11 +54,11 @@
54 54
     margin: 10px auto 0;
55 55
   }
56 56
 
57
-  &--t-community-user-graygreen-darker {
58
-    background-color: @community-user-graygreen-darker;
57
+  &--t-greyseafoam-dark {
58
+    background-color: @greyseafoam-dark;
59 59
 
60 60
     &:hover {
61
-      background-color: @community-user-graygreen-dark;
61
+      background-color: @greyseafoam;
62 62
     };
63 63
   }
64 64
 
@@ -70,8 +70,8 @@
70 70
     };
71 71
   }
72 72
 
73
-  &--t-dark-purple-darker {
74
-    background-color: @dark-purple-darker;
73
+  &--t-greyviolet-darker {
74
+    background-color: @greyviolet-darker;
75 75
 
76 76
     &:hover {
77 77
       background-color: #000;
@@ -97,8 +97,8 @@
97 97
   &[disabled], &.js-disabled {
98 98
     color: #ccc;
99 99
 
100
-    &.@{_top}--t-community-user-graygreen-darker {
101
-      background-color: @community-user-graygreen-darker;
100
+    &.@{_top}--t-greyseafoam-dark {
101
+      background-color: @greyseafoam-dark;
102 102
     }
103 103
 
104 104
     &.@{_top}--t-ddd {
@@ -110,8 +110,8 @@
110 110
       background-color: @dark-purple-dark;
111 111
     }
112 112
 
113
-    &.@{_top}--t-dark-purple-darker {
114
-      background-color: @dark-purple-darker;
113
+    &.@{_top}--t-greyviolet-darker {
114
+      background-color: @greyviolet-darker;
115 115
     }
116 116
   }
117 117
 

+ 1
- 1
resources/assets/less/bem/sort.less View File

@@ -22,7 +22,7 @@
22 22
   @_hover-bg: #ddd;
23 23
   @_hover-bg--beatmapsets: #eee;
24 24
   @_hover-bg--beatmapset-discussions: #eee;
25
-  @_hover-bg--changelog: @dark-purple-darker;
25
+  @_hover-bg--changelog: @greyviolet-darker;
26 26
   @_hover-bg--forum-topics: #ddd;
27 27
 
28 28
   .default-gutter();

+ 1
- 1
resources/assets/less/bem/value-display.less View File

@@ -50,7 +50,7 @@
50 50
 
51 51
   &__description {
52 52
     font-size: @font-size--title-small;
53
-    color: @community-user-graygreen-lighter;
53
+    color: @greyseafoam-lighter;
54 54
 
55 55
     a {
56 56
       color: @community-user-graygreen-light;

+ 51
- 18
resources/assets/less/colors.less View File

@@ -80,17 +80,14 @@
80 80
 @gray-blue-dark: #293336;
81 81
 @gray-blue-darker: #181f21;
82 82
 
83
-@dark-purple-lighter: #ebb8fe;
84 83
 @dark-purple-light: #cba4da;
85
-@dark-purple: #bf04ff;
86 84
 @dark-purple-dark: #312436;
87
-@dark-purple-darker: #201823;
88 85
 @dark-graypurple: #584261;
89 86
 
90 87
 // osu!palette 2017(?)
91 88
 @gray: #262626;
92
-@gray-dark: #1A1A1A;
93
-@gray-darker: #0E0E0E;
89
+@gray-dark: #1a1a1a;
90
+@gray-darker: #0e0e0e;
94 91
 @light-gray: #f5f5f5;
95 92
 
96 93
 // osu!palette 2018 :rolling_eyes:
@@ -106,21 +103,57 @@
106 103
 @community-grayblue-tile-background: #323E41;
107 104
 @community-grayblue-text: #93B8C4;
108 105
 
109
-@community-user-green-light: #deff87;
110
-@community-user-green: #05ffa2;
111 106
 @community-user-green-dark: #a6cc00;
112
-@community-user-graygreen-lighter: #9ebab1;
113 107
 @community-user-graygreen-light: #77998e;
114
-@community-user-graygreen: #4e7466;
115
-@community-user-graygreen-dark: #33413c;
116
-@community-user-graygreen-darker: #2c3532;
117
-@community-user-graygreen-darkest: #1e2422;
118
-
119
-// 2019
120
-@graysky-light: #8ab3cc;
121
-@graysky: #405461;
122
-@graysky-dark: #303d47;
123
-@graysky-darker: #21272c;
108
+
109
+// osu!palette 2019
110
+@red-lighter: #ffcaca;
111
+@red-light: #ff8989;
112
+@red: #eb5757;
113
+@red-dark: #af4646;
114
+@red-darker: #643232;
115
+
116
+@sky: #6bb5ff;
117
+@greysky-lighter: #c6e3f4;
118
+@greysky-light: #8ab3cc;
119
+@greysky: #405461;
120
+@greysky-dark: #303d47;
121
+@greysky-darker: #21272c;
122
+
123
+@seafoam: #05ffa2;
124
+@greyseafoam-lighter: #9ebab1;
125
+@greyseafoam-light: #4d7365;
126
+@greyseafoam: #33413c;
127
+@greyseafoam-dark: #2c3532;
128
+@greyseafoam-darker: #1e2422;
129
+
130
+@cyan: #05f4fd;
131
+@greycyan-lighter: #77b1b3;
132
+@greycyan-light: #436d6f;
133
+@greycyan: #293d3e;
134
+@greycyan-dark: #243536;
135
+@greycyan-darker: #1e2929;
136
+@greylime-lighter: #deff87;
137
+
138
+@lime: #82ff05;
139
+@greylime-light: #657259;
140
+@greylime: #3f443a;
141
+@greylime-dark: #32352e;
142
+@greylime-darker: #2e302b;
143
+
144
+@violet: #bf04ff;
145
+@greyviolet-lighter: #ebb8fe;
146
+@greyviolet-light: #685370;
147
+@greyviolet: #46334d;
148
+@greyviolet-dark: #2c2230;
149
+@greyviolet-darker: #201823;
150
+
151
+@carmine: #ff0542;
152
+@greycarmine-lighter: #deaab4;
153
+@greycarmine-light: #644f53;
154
+@greycarmine: #342b2d;
155
+@greycarmine-dark: #302a2b;
156
+@greycarmine-darker: #241d1e;
124 157
 
125 158
 .colors(@name) {
126 159
   @lighter: "@{name}-lighter";

+ 1
- 1
resources/assets/less/variables.less View File

@@ -270,7 +270,7 @@
270 270
 @changelog-stream--wiki: #999;
271 271
 
272 272
 @changelog-bg: @dark-purple-dark;
273
-@changelog-bg-extra: @dark-purple-darker;
273
+@changelog-bg-extra: @greyviolet-darker;
274 274
 @changelog-border-bg: fade(@changelog-bg-extra, 50%);
275 275
 @changelog-fg: #fff;
276 276
 @changelog-fg-content: @pink-lighter;

+ 1
- 1
resources/lang/be/accounts.php View File

@@ -20,7 +20,7 @@
20 20
 
21 21
 return [
22 22
     'edit' => [
23
-        'title' => 'Налады <strong>акаўнта</strong>',
23
+        'title' => 'Налады <strong>ўлік. запісу</strong>',
24 24
         'title_compact' => 'налады',
25 25
         'username' => 'імя карыстальніка',
26 26
 

+ 1
- 0
resources/lang/en/authorization.php View File

@@ -119,6 +119,7 @@ return [
119 119
             'vote' => [
120 120
                 'no_forum_access' => 'Access to requested forum is required.',
121 121
                 'over' => 'Polling is over and can not be voted on anymore.',
122
+                'play_more' => 'You need to play more before voting on forum.',
122 123
                 'voted' => 'Changing vote is not allowed.',
123 124
 
124 125
                 'user' => [

+ 3
- 0
resources/lang/en/forum.php View File

@@ -179,6 +179,8 @@ return [
179 179
             ],
180 180
 
181 181
             'poll' => [
182
+                'hide_results' => 'Hide the results of the poll.',
183
+                'hide_results_info' => 'They will be shown only after the poll concludes.',
182 184
                 'length' => 'Run poll for',
183 185
                 'length_days_suffix' => 'days',
184 186
                 'length_info' => 'Leave blank for a never ending poll',
@@ -292,6 +294,7 @@ return [
292 294
                 'detail' => [
293 295
                     'end_time' => 'Polling will end at :time',
294 296
                     'ended' => 'Polling ended :time',
297
+                    'results_hidden' => 'Results will be shown after polling ends.',
295 298
                     'total' => 'Total votes: :count',
296 299
                 ],
297 300
             ],

+ 3
- 1
resources/lang/en/model_validation.php View File

@@ -73,7 +73,8 @@ return [
73 73
 
74 74
         'topic_poll' => [
75 75
             'duplicate_options' => 'Duplicated option is not allowed.',
76
-            'grace_period_expired' => 'Can\'t edit a poll after more than :limit hours',
76
+            'grace_period_expired' => 'Can\'t edit a poll after more than :limit hours.',
77
+            'hiding_results_forever' => 'Can\'t hide results of a poll that never ends.',
77 78
             'invalid_max_options' => 'Option per user may not exceed the number of available options.',
78 79
             'minimum_one_selection' => 'A minimum of one option per user is required.',
79 80
             'minimum_two_options' => 'Need at least two options.',
@@ -111,6 +112,7 @@ return [
111 112
         'too_long' => 'Exceeded maximum length - can only be up to :limit characters.',
112 113
 
113 114
         'change_username' => [
115
+            'restricted' => 'You cannot change your username while restricted.',
114 116
             'supporter_required' => [
115 117
                 '_' => 'You must have :link to change your name!',
116 118
                 'link_text' => 'supported osu!',

+ 1
- 1
resources/lang/es/multiplayer.php View File

@@ -61,7 +61,7 @@ return [
61 61
         ],
62 62
 
63 63
         'teams' => [
64
-            'blue' => 'Equipo Azúl',
64
+            'blue' => 'Equipo Azul',
65 65
             'red' => 'Equipo Rojo',
66 66
         ],
67 67
     ],

+ 1
- 1
resources/lang/es/report.php View File

@@ -20,7 +20,7 @@
20 20
 
21 21
 return [
22 22
     'scores' => [
23
-        'button' => 'Puntuación del reporte',
23
+        'button' => 'Reportar puntuación',
24 24
         'title' => 'mostrar puntuación de :username ?',
25 25
     ],
26 26
 

+ 1
- 1
resources/lang/ja/beatmappacks.php View File

@@ -25,7 +25,7 @@ return [
25 25
             'instruction' => [
26 26
                 '_' => "インストールするには:ダウンロードが完了次第.rarファイルをosu!のSongsフォルダに中身を解凍してください。
27 27
                     osu!が次回ビートマップの読み込みを開始する時に圧縮されているビートマップファイル(zip/osz)を自動的に取り込みます。
28
-                    :scaryにzip/oszファイルを手動で解凍しないでください。譜面の取り込みに失敗して正常に譜面が表示されなくなります。",
28
+                    :scaryにzip/oszファイルを手動で解凍しないでください。ビートマップの取り込みに失敗して正常にビートマップが表示されなくなります。",
29 29
                 'scary' => '絶対に',
30 30
             ],
31 31
             'note' => [

+ 8
- 8
resources/lang/ja/users.php View File

@@ -19,18 +19,18 @@
19 19
  */
20 20
 
21 21
 return [
22
-    'deleted' => '[削除されたユーザーです]',
22
+    'deleted' => '[削除されたユーザー]',
23 23
 
24 24
     'beatmapset_activities' => [
25 25
         'title' => ":userのModding履歴",
26 26
         'title_compact' => 'Modding',
27 27
 
28 28
         'discussions' => [
29
-            'title_recent' => '最近ディスカッション',
29
+            'title_recent' => '最近開始されたディスカッション',
30 30
         ],
31 31
 
32 32
         'events' => [
33
-            'title_recent' => '最近の出来事',
33
+            'title_recent' => '最近のイベント',
34 34
         ],
35 35
 
36 36
         'posts' => [
@@ -71,11 +71,11 @@ return [
71 71
         'password' => 'パスワード',
72 72
         'button' => 'ログイン',
73 73
         'button_posting' => 'ログイン中・・・',
74
-        'remember' => 'ログイン状態を保する',
74
+        'remember' => 'ログイン状態を保する',
75 75
         'title' => '続行するにはログインが必要です',
76
-        'failed' => 'サインインに失敗しました',
77
-        'register' => "osu!アカウントがない方はこちらから",
78
-        'forgot' => 'パスワードを紛失した場合',
76
+        'failed' => 'ログインに失敗しました',
77
+        'register' => "osu!アカウントを持っていませんか?新しいアカウントを作るにはこちらから",
78
+        'forgot' => 'パスワードを忘れましたか?',
79 79
         'beta' => [
80 80
             'main' => 'ベータアクセスは権限があるユーザーのみに付与されます',
81 81
             'small' => '(osu!サポーターは間もなくもらえます)',
@@ -85,7 +85,7 @@ return [
85 85
     ],
86 86
 
87 87
     'posts' => [
88
-        'title' => ':username\の投稿',
88
+        'title' => ':usernameの投稿',
89 89
     ],
90 90
 
91 91
     'signup' => [

+ 3
- 3
resources/lang/ja/wiki.php View File

@@ -20,11 +20,11 @@
20 20
 
21 21
 return [
22 22
     'show' => [
23
-        'fallback_translation' => '要求されたページはまだ選択された言語(:language)に翻訳されていません。英語版を表示します。',
23
+        'fallback_translation' => '要求されたページは選択された言語(:language)に翻訳されていません。英語版を表示します。',
24 24
         'languages' => '言語',
25 25
         'missing' => '要求されたページ”:keyword”は見つかりませんでした。',
26 26
         'missing_title' => 'Not Found',
27
-        'missing_translation' => '要求されたページはまだ現在選択している言語に翻訳されていません。',
27
+        'missing_translation' => '要求されたページは選択している言語に翻訳されていません。',
28 28
         'search' => '既存のページで:linkを検索する',
29 29
         'toc' => '目次',
30 30
 
@@ -34,7 +34,7 @@ return [
34 34
         ],
35 35
 
36 36
         'translation' => [
37
-            'legal' => 'この翻訳は一時的に提供されています。:defaultはこの文章の原文です。',
37
+            'legal' => 'この翻訳は便宜上提供されています。:defaultが法的拘束力のある文章です。',
38 38
             'outdated' => 'このページには古い翻訳情報が含まれています。最新情報を確認するには:defaultを参照してください。(可能であれば翻訳にご協力ください)',
39 39
 
40 40
             'default' => '英語版',

+ 1
- 1
resources/lang/pl/api.php View File

@@ -27,7 +27,7 @@ return [
27 27
     ],
28 28
 
29 29
     'scopes' => [
30
-        'identify' => 'Zdefiniuj się i przeczytaj swój publiczny profil.',
30
+        'identify' => '',
31 31
 
32 32
         'friends' => [
33 33
             'read' => 'Zobacz, kogo obserwujesz.',

+ 2
- 2
resources/lang/pl/beatmaps.php View File

@@ -197,7 +197,7 @@ return [
197 197
                 'title' => 'Tytuł',
198 198
                 'artist' => 'Wykonawca',
199 199
                 'difficulty' => 'Poziom trudności',
200
-                'favourites' => 'Ulubione',
200
+                'favourites' => 'Polubienia',
201 201
                 'updated' => 'Ostatnie aktualizacje',
202 202
                 'ranked' => 'Data',
203 203
                 'rating' => 'Ocena',
@@ -305,6 +305,6 @@ return [
305 305
     ],
306 306
     'panel' => [
307 307
         'playcount' => 'Liczba zagrań: :count',
308
-        'favourites' => 'Polubiło: :count',
308
+        'favourites' => 'Liczba polubień: :count',
309 309
     ],
310 310
 ];

+ 1
- 1
resources/lang/pl/common.php View File

@@ -67,7 +67,7 @@ return [
67 67
         'hours' => ':count_delimited godzina|:count_delimited godziny|:count_delimited godzin',
68 68
         'item' => ':count_delimited sztuka|:count_delimited sztuki|:count_delimited sztuk',
69 69
         'minute_short_unit' => 'min|min|min',
70
-        'minutes' => ':count minuta|:count minuty|:count minut',
70
+        'minutes' => ':count_delimited minuta|:count_delimited minuty|:count_delimited minut',
71 71
         'months' => ':count_delimited miesiąc|:count_delimited miesiące|:count_delimited miesięcy',
72 72
         'second_short_unit' => 's|s|s',
73 73
         'years' => ':count_delimited rok|:count_delimited lata|:count_delimited lat',

+ 2
- 2
resources/lang/pl/contest.php View File

@@ -58,8 +58,8 @@ return [
58 58
     ],
59 59
     'vote' => [
60 60
         'list' => 'głosy',
61
-        'count' => ':count głos|:count głosy|:count głosów',
62
-        'points' => ':count punkt|:count punkty|:count punktów',
61
+        'count' => ':count_delimited głos|:count_delimited głosy|:count_delimited głosów',
62
+        'points' => ':count_delimited punkt|:count_delimited punkty|:count_delimited punktów',
63 63
     ],
64 64
     'dates' => [
65 65
         'ended' => 'Zakończony :date',

+ 1
- 1
resources/lang/pl/fulfillments.php View File

@@ -24,7 +24,7 @@ return [
24 24
             'subject' => 'Dziękujemy, osu! cię <3',
25 25
         ],
26 26
         'supporter_gift' => [
27
-            'subject' => 'Ktoś podarował ci status donatora osu!',
27
+            'subject' => 'Otrzymujesz status donatora osu!',
28 28
         ],
29 29
     ],
30 30
 ];

+ 1
- 1
resources/lang/pl/model_validation.php View File

@@ -98,7 +98,7 @@ return [
98 98
         'username_available_soon' => 'Ta nazwa użytkownika będzie dostępna niedługo!',
99 99
         'username_invalid_characters' => 'Ta nazwa użytkownika zawiera nieprawidłowe znaki.',
100 100
         'username_in_use' => 'Ta nazwa użytkownika jest już w użyciu!',
101
-        'username_locked' => 'Ta nazwa użytkownika jest już zajęta!', // TODO: language for this should be slightly different.
101
+        'username_locked' => 'Ta nazwa użytkownika jest już w użyciu!', // TODO: language for this should be slightly different.
102 102
         'username_no_space_userscore_mix' => 'Używaj spacji albo znaków podkreślenia, nie obu naraz!',
103 103
         'username_no_spaces' => "Nazwa użytkownika nie może zaczynać się ani kończyć spacjami!",
104 104
         'username_not_allowed' => 'Ta nazwa użytkownika nie jest dozwolona.',

+ 1
- 1
resources/lang/pl/model_validation/store/product.php View File

@@ -20,7 +20,7 @@
20 20
 
21 21
 return [
22 22
     'insufficient_stock' => 'Niewystarczająca liczba produktów!',
23
-    'must_separate' => 'Ten przedmiot musi zostać opłacony oddzielnie od pozostałych',
23
+    'must_separate' => 'Ten przedmiot musi zostać zamówiony oddzielnie od pozostałych',
24 24
     'not_available' => 'Ten produkt nie jest dostępny.',
25 25
     'too_many' => 'Możesz dodać tylko :count szt. tego produktu dla każdego zamówienia.',
26 26
 ];

+ 1
- 1
resources/lang/pl/news.php View File

@@ -34,7 +34,7 @@ return [
34 34
     ],
35 35
 
36 36
     'show' => [
37
-        'by' => 'autorstwa :user',
37
+        'by' => 'od :user',
38 38
 
39 39
         'nav' => [
40 40
             'newer' => 'Nowsza wiadomość',

+ 2
- 2
resources/lang/pl/oauth.php View File

@@ -23,7 +23,7 @@ return [
23 23
 
24 24
     'authorise' => [
25 25
         'authorise' => 'Autoryzuj',
26
-        'request' => 'pyta o pozwolenie na dostęp do twojego konta.',
26
+        'request' => 'żąda pozwolenia na dostęp do twojego konta.',
27 27
         'scopes_title' => 'Ta aplikacja będzie mogła:',
28 28
         'title' => 'Prośba o autoryzację',
29 29
 
@@ -35,7 +35,7 @@ return [
35 35
 
36 36
     'login' => [
37 37
         'download' => 'Kliknij tutaj, aby pobrać grę i utworzyć konto.',
38
-        'label' => 'Na początek zalogujmy się na twoje konto!',
38
+        'label' => 'Na początek zaloguj się na swoje konto!',
39 39
         'title' => 'Logowanie do konta',
40 40
     ],
41 41
 ];

+ 1
- 1
resources/lang/pl/report.php View File

@@ -26,6 +26,6 @@ return [
26 26
 
27 27
     'comment' => [
28 28
         'button' => 'Zgłoś',
29
-        'title' => 'Zgłosić komentarz :username?',
29
+        'title' => 'Zgłosić komentarz gracza :username?',
30 30
     ],
31 31
 ];

+ 1
- 1
resources/lang/pl/store.php View File

@@ -90,7 +90,7 @@ return [
90 90
 
91 91
         'invoice' => 'Pokaż fakturę',
92 92
         'no_orders' => 'Brak zamówień do wyświetlenia.',
93
-        'resume' => 'Kontynuuj zamówienie',
93
+        'resume' => 'Wznów zamówienie',
94 94
 
95 95
         'item' => [
96 96
             'display_name' => [

+ 1
- 1
resources/lang/pt-br/comments.php View File

@@ -35,7 +35,7 @@ return [
35 35
 
36 36
     'editor' => [
37 37
         'textarea_hint' => [
38
-            '_' => 'Pressione enter para :action, use Shift+Enter para adicionar nova uma nova linha.',
38
+            '_' => 'Pressione enter para :action, use Shift+Enter para adicionar uma nova linha.',
39 39
             'edit' => 'salvar',
40 40
             'new' => 'publicar',
41 41
             'reply' => 'responder',

+ 1
- 1
resources/lang/pt/beatmaps.php View File

@@ -197,7 +197,7 @@ return [
197 197
                 'title' => 'Título',
198 198
                 'artist' => 'Artista',
199 199
                 'difficulty' => 'Dificuldade',
200
-                'favourites' => '',
200
+                'favourites' => 'Favoritos',
201 201
                 'updated' => 'Atualizado',
202 202
                 'ranked' => 'Classificado',
203 203
                 'rating' => 'Avaliação',

+ 1
- 1
resources/lang/pt/errors.php View File

@@ -35,7 +35,7 @@ return [
35 35
         'standard_converts_only' => 'Não há pontuações disponíveis para o modo solicitado nesta dificuldade de beatmap.',
36 36
     ],
37 37
     'checkout' => [
38
-        'generic' => '',
38
+        'generic' => 'Ocorreu um erro ao preparar o teu pagamento.',
39 39
     ],
40 40
     'logged_out' => 'Foste desconectado. Por favor inicia sessão e tenta outra vez.',
41 41
     'supporter_only' => 'Tu tens de ser um apoiante para utilizar esta funcionalidade.',

+ 2
- 2
resources/lang/pt/layout.php View File

@@ -127,8 +127,8 @@ return [
127 127
             'modding-history-posts' => 'publicações de modificações do utilizador',
128 128
             'modding-history-votesGiven' => 'votos de modificações do utilizador dados',
129 129
             'modding-history-votesReceived' => 'votos de modificações do utilizador recebidos',
130
-            'oauth_login' => '',
131
-            'oauth_request' => '',
130
+            'oauth_login' => 'inicia sessão para oauth',
131
+            'oauth_request' => 'autorização oauth',
132 132
             'settings' => 'Definições',
133 133
         ],
134 134
         'store' => [

+ 1
- 1
resources/lang/pt/model_validation/store/product.php View File

@@ -20,7 +20,7 @@
20 20
 
21 21
 return [
22 22
     'insufficient_stock' => 'Já não resta quantidade suficiente para este item!',
23
-    'must_separate' => '',
23
+    'must_separate' => 'Este item tem que ser verificado separadamente de outros itens',
24 24
     'not_available' => 'Este item não está disponível.',
25 25
     'too_many' => 'Só podes encomendar :count deste item por compra.',
26 26
 ];

+ 10
- 10
resources/lang/pt/oauth.php View File

@@ -19,23 +19,23 @@
19 19
  */
20 20
 
21 21
 return [
22
-    'cancel' => '',
22
+    'cancel' => 'Cancelar',
23 23
 
24 24
     'authorise' => [
25
-        'authorise' => '',
26
-        'request' => '',
27
-        'scopes_title' => '',
28
-        'title' => '',
25
+        'authorise' => 'Autorizar',
26
+        'request' => 'está a solicitar permissão para aceder à tua conta.',
27
+        'scopes_title' => 'Esta aplicação será capaz de:',
28
+        'title' => 'Pedido de Autorização',
29 29
 
30 30
         'wrong_user' => [
31
-            '_' => '',
32
-            'logout_link' => '',
31
+            '_' => 'Estás autenticado como :user. :logout_link.',
32
+            'logout_link' => 'Clica aqui para iniciar sessão como um diferente utilizador',
33 33
         ],
34 34
     ],
35 35
 
36 36
     'login' => [
37
-        'download' => '',
38
-        'label' => '',
39
-        'title' => '',
37
+        'download' => 'Clica aqui para transferir o jogo e criar uma conta',
38
+        'label' => 'Primeiro, vamos entrar na tua conta!',
39
+        'title' => 'Login da Conta',
40 40
     ],
41 41
 ];

+ 1
- 1
resources/lang/pt/store.php View File

@@ -90,7 +90,7 @@ return [
90 90
 
91 91
         'invoice' => 'Ver Fatura',
92 92
         'no_orders' => 'Sem pedidos para ver.',
93
-        'resume' => '',
93
+        'resume' => 'Retomar Pagamento',
94 94
 
95 95
         'item' => [
96 96
             'display_name' => [

+ 1
- 1
resources/lang/ru/oauth.php View File

@@ -25,7 +25,7 @@ return [
25 25
         'authorise' => 'Авторизация',
26 26
         'request' => 'запрашивает разрешения доступа к вашему аккаунту.',
27 27
         'scopes_title' => 'Это приложение сможет:',
28
-        'title' => 'Запрос Авторизации',
28
+        'title' => 'Запрос авторизации',
29 29
 
30 30
         'wrong_user' => [
31 31
             '_' => 'Вы вошли как :user. :logout_link.',

+ 1
- 1
resources/lang/zh-tw/api.php View File

@@ -30,7 +30,7 @@ return [
30 30
         'identify' => '識別您的身份並閱讀您的公開個人資料。',
31 31
 
32 32
         'friends' => [
33
-            'read' => '',
33
+            'read' => '查看您追蹤的玩家們。',
34 34
         ],
35 35
     ],
36 36
 ];

+ 4
- 4
resources/lang/zh-tw/beatmaps.php View File

@@ -101,9 +101,9 @@ return [
101 101
 
102 102
         'sort' => [
103 103
             '_' => '排序:',
104
-            'created_at' => '',
104
+            'created_at' => '建立時間',
105 105
             'timeline' => '',
106
-            'updated_at' => '',
106
+            'updated_at' => '最後更新',
107 107
         ],
108 108
 
109 109
         'stats' => [
@@ -196,12 +196,12 @@ return [
196 196
             'sorting' => [
197 197
                 'title' => '',
198 198
                 'artist' => '',
199
-                'difficulty' => '',
199
+                'difficulty' => '難度',
200 200
                 'favourites' => '',
201 201
                 'updated' => '',
202 202
                 'ranked' => '',
203 203
                 'rating' => '',
204
-                'plays' => '',
204
+                'plays' => '遊玩次數',
205 205
                 'relevance' => '',
206 206
                 'nominations' => '',
207 207
             ],

+ 5
- 5
resources/lang/zh-tw/chat.php View File

@@ -20,7 +20,7 @@
20 20
 
21 21
 return [
22 22
     'coming_soon' => '即將上線',
23
-    'limitation_notice' => '',
23
+    'limitation_notice' => '注意: 只有使用 <a href=":lazer_link">osu! lazer</a> 或新網站的人才能在此系統收到私訊。<br/>如果您不確定, 請使用 <a href=":oldpm_link">舊論壇</a> 私訊他們。',
24 24
     'talking_in' => '在 :channel 聊天',
25 25
     'talking_with' => '與 :name 聊天',
26 26
     'title_compact' => '聊天',
@@ -30,10 +30,10 @@ return [
30 30
         'user' => '您現在無法對這個玩家發送訊息。可能是Bug或是以下原因:',
31 31
         'reasons' => [
32 32
             'blocked' => '您已被收件者封鎖了',
33
-            'channel_moderated' => '',
33
+            'channel_moderated' => '此頻道已經被管理員接管。',
34 34
             'friends_only' => '收件者只接受朋友發送的訊息',
35
-            'restricted' => '',
36
-            'target_restricted' => '',
35
+            'restricted' => '您的帳號已被限制。',
36
+            'target_restricted' => '該使用者的帳號已被限制。',
37 37
         ],
38 38
     ],
39 39
     'input' => [
@@ -43,7 +43,7 @@ return [
43 43
     ],
44 44
     'no-conversations' => [
45 45
         'howto' => "",
46
-        'lazer' => '',
46
+        'lazer' => '您通過 <a href=":link">osu! lazer</a> 加入的公開頻道也會顯示在這裡。',
47 47
         'pm_limitations' => '只有使用 <a href=":link">osu! lazer</a> 或是新網站的人才會收到才會收到訊息',
48 48
         'title' => '還沒有聊天過',
49 49
     ],

+ 2
- 2
resources/lang/zh-tw/comments.php View File

@@ -43,8 +43,8 @@ return [
43 43
     ],
44 44
 
45 45
     'guest_button' => [
46
-        'new' => '',
47
-        'reply' => '',
46
+        'new' => '登入以留言。',
47
+        'reply' => '登入以回覆。',
48 48
     ],
49 49
 
50 50
     'index' => [

+ 5
- 5
resources/lang/zh-tw/contest.php View File

@@ -21,7 +21,7 @@
21 21
 return [
22 22
     'header' => [
23 23
         'small' => '享受遊戲以外的競賽體驗。',
24
-        'large' => '',
24
+        'large' => '社群賽事',
25 25
     ],
26 26
     'voting' => [
27 27
         'over' => '這場評選的投票已經結束',
@@ -32,9 +32,9 @@ return [
32 32
         ],
33 33
 
34 34
         'button' => [
35
-            'add' => '',
36
-            'remove' => '',
37
-            'used_up' => '',
35
+            'add' => '投他一票',
36
+            'remove' => '取消投票',
37
+            'used_up' => '您的票已經用光了。',
38 38
         ],
39 39
     ],
40 40
     'entry' => [
@@ -45,7 +45,7 @@ return [
45 45
         'over' => '感謝參與!提交已經關閉,投票即將開始。',
46 46
         'limit_reached' => '您提交的參賽文件大小超出限制',
47 47
         'drop_here' => '將您的參賽文件拖到此處',
48
-        'download' => '',
48
+        'download' => '下載 .osz 檔案',
49 49
         'wrong_type' => [
50 50
             'art' => '只接受 .jpg 和 .png 格式的文件.',
51 51
             'beatmap' => '只接受 .osu 格式的文件.',

+ 1
- 1
resources/lang/zh-tw/errors.php View File

@@ -35,7 +35,7 @@ return [
35 35
         'standard_converts_only' => '此遊戲模式下的圖譜難度尚未有分數。',
36 36
     ],
37 37
     'checkout' => [
38
-        'generic' => '',
38
+        'generic' => '處理您的訂單時發生錯誤。',
39 39
     ],
40 40
     'logged_out' => '您已登出,請登入後再試。',
41 41
     'supporter_only' => '您需要成為 osu!贊助者才能使用此功能 。',

+ 1
- 1
resources/lang/zh-tw/fulfillments.php View File

@@ -24,7 +24,7 @@ return [
24 24
             'subject' => '非常感謝,osu! 愛你哦~',
25 25
         ],
26 26
         'supporter_gift' => [
27
-            'subject' => '',
27
+            'subject' => '您已獲贈 osu!supporter 標籤!',
28 28
         ],
29 29
     ],
30 30
 ];

+ 1
- 1
resources/lang/zh-tw/model_validation/fulfillments.php View File

@@ -25,6 +25,6 @@ return [
25 25
         'reverting_username_mismatch' => '目前的使用者名稱(:current)與要撤銷變更的使用者名稱不一致(:username)',
26 26
     ],
27 27
     'supporter_tag' => [
28
-        'insufficient_paid' => '',
28
+        'insufficient_paid' => '要獲得 osu!supporter 的標籤,你的贊助金額還不夠喔。 (:actual > :expected)',
29 29
     ],
30 30
 ];

+ 2
- 2
resources/lang/zh-tw/model_validation/store/product.php View File

@@ -19,8 +19,8 @@
19 19
  */
20 20
 
21 21
 return [
22
-    'insufficient_stock' => '沒有足夠的這個物品了!',
23
-    'must_separate' => '',
22
+    'insufficient_stock' => '這個物品沒有存貨了!',
23
+    'must_separate' => '這件物品必須和其他物品分開結帳。',
24 24
     'not_available' => '此物品不可用。',
25 25
     'too_many' => '每個訂單只能訂購本物品 :count 個',
26 26
 ];

+ 0
- 0
resources/lang/zh-tw/oauth.php View File


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save