diff --git a/.circleci/config.yml b/.circleci/config.yml
index 4307bf0..0c04d93 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -57,6 +57,12 @@ jobs:
- store_test_results:
path: /tmp/php-cs-fixer
+ # Run stylelint
+ - run:
+ name: stylelint
+ command: yarn run stylelint
+ when: always
+
# Run unit test
- run:
command: |
diff --git a/.gitignore b/.gitignore
index 55d3da6..323d21f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@
/storage/*.key
/vendor
/.idea
+/.vscode
/.vagrant
Homestead.json
Homestead.yaml
diff --git a/Dockerfile b/Dockerfile
index 65d49a8..565fba1 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,3 +1,5 @@
+FROM node:10-jessie as node
+
FROM php:7.1-apache
ENV APACHE_DOCUMENT_ROOT /var/www/html/public
@@ -15,6 +17,16 @@ RUN apt-get update \
COPY dist/bin /usr/local/bin/
COPY dist/php.d /usr/local/etc/php/php.d/
+COPY --from=node /usr/local/bin/node /usr/local/bin/
+COPY --from=node /usr/local/lib/node_modules /usr/local/lib/node_modules
+COPY --from=node /opt/yarn-* /opt/yarn
+
+RUN ln -s /opt/yarn/bin/yarn /usr/local/bin/yarn \
+ && ln -s ../lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
+ && ln -s ../lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx
+
+
+
ENTRYPOINT ["tissue-entrypoint.sh"]
CMD ["apache2-foreground"]
diff --git a/README.md b/README.md
index 7953628..8bd1e80 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ a.k.a. shikorism.net
## 構成
- Laravel 5.5
-- Bootstrap 4.2.1
+- Bootstrap 4.3.1
## 実行環境
@@ -33,10 +33,11 @@ docker-compose build
docker-compose up -d
```
-4. Composer を使い必要なライブラリをインストールします。
+4. Composer と yarn を使い必要なライブラリをインストールします。
```
docker-compose exec web composer install
+docker-compose exec web yarn install
```
5. 暗号化キーの作成と、データベースのマイグレーションを行います。
@@ -52,7 +53,14 @@ docker-compose exec web php artisan migrate
docker-compose exec web chown -R www-data /var/www/html
```
-7. 最後に `.env` を読み込み直すために起動し直します。
+7. アセットをビルドします。
+
+```
+docker-compose exec web yarn dev
+```
+
+
+8. 最後に `.env` を読み込み直すために起動し直します。
```
docker-compose up -d
@@ -68,6 +76,16 @@ docker-compose -f docker-compose.yml -f docker-compose.debug.yml up -d
で起動することにより、DB のポート`5432`を開放してホストマシンから接続できるようになります。
+## アセットのリアルタイムビルド
+`yarn watch`を使うとソースファイルを監視して差分があると差分ビルドしてくれます。フロント開発時は活用しましょう。
+```
+docker-compose run --rm web yarn watch
+```
+
+もしファイル変更時に更新されない場合は`yarn watch-poll`を試してみてください。
+現在Docker環境でのHMRはサポートしてません。Docker外ならおそらく動くでしょう。
+その他詳しくはlaravel-mixのドキュメントなどを当たってください。
+
## 環境構築上の諸注意
- 初版時点では、DB サーバとして PostgreSQL を使うよう .env ファイルを設定するくらいです。
diff --git a/app/Console/Commands/DemoteUser.php b/app/Console/Commands/DemoteUser.php
new file mode 100644
index 0000000..94dfe58
--- /dev/null
+++ b/app/Console/Commands/DemoteUser.php
@@ -0,0 +1,61 @@
+argument('username'))->first();
+ if ($user === null) {
+ $this->error('No user with such username');
+
+ return 1;
+ }
+
+ if (!$user->is_admin) {
+ $this->info('@' . $user->name . ' is already an user.');
+
+ return 0;
+ }
+
+ $user->is_admin = false;
+ if ($user->save()) {
+ $this->info('@' . $user->name . ' is an user now.');
+ } else {
+ $this->error('Something happened.');
+ }
+ }
+}
diff --git a/app/Console/Commands/PromoteUser.php b/app/Console/Commands/PromoteUser.php
new file mode 100644
index 0000000..b737ca5
--- /dev/null
+++ b/app/Console/Commands/PromoteUser.php
@@ -0,0 +1,61 @@
+argument('username'))->first();
+ if ($user === null) {
+ $this->error('No user with such username');
+
+ return 1;
+ }
+
+ if ($user->is_admin) {
+ $this->info('@' . $user->name . ' is already an administrator.');
+
+ return 0;
+ }
+
+ $user->is_admin = true;
+ if ($user->save()) {
+ $this->info('@' . $user->name . ' is an administrator now.');
+ } else {
+ $this->error('Something happened.');
+ }
+ }
+}
diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php
index 622e774..da3f264 100644
--- a/app/Console/Kernel.php
+++ b/app/Console/Kernel.php
@@ -2,6 +2,8 @@
namespace App\Console;
+use App\Console\Commands\DemoteUser;
+use App\Console\Commands\PromoteUser;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
@@ -35,6 +37,8 @@ class Kernel extends ConsoleKernel
*/
protected function commands()
{
+ $this->load(__DIR__.'/Commands');
+
require base_path('routes/console.php');
}
}
diff --git a/app/Http/Controllers/Admin/DashboardController.php b/app/Http/Controllers/Admin/DashboardController.php
new file mode 100644
index 0000000..b09d0c8
--- /dev/null
+++ b/app/Http/Controllers/Admin/DashboardController.php
@@ -0,0 +1,14 @@
+select('id', 'category', 'pinned', 'title', 'created_at')
+ ->orderByDesc('pinned')
+ ->orderByDesc('created_at')
+ ->paginate(20);
+
+ return view('admin.info.index')->with([
+ 'informations' => $informations,
+ 'categories' => Information::CATEGORIES
+ ]);
+ }
+
+ public function create()
+ {
+ return view('admin.info.create')->with([
+ 'categories' => Information::CATEGORIES
+ ]);
+ }
+
+ public function store(AdminInfoStoreRequest $request)
+ {
+ $inputs = $request->all();
+ if (!$request->has('pinned')) {
+ $inputs['pinned'] = false;
+ }
+
+ $info = Information::create($inputs);
+
+ return redirect()->route('admin.info.edit', ['info' => $info])->with('status', 'お知らせを更新しました。');
+ }
+
+ public function edit($id)
+ {
+ $information = Information::findOrFail($id);
+
+ return view('admin.info.edit')->with([
+ 'info' => $information,
+ 'categories' => Information::CATEGORIES
+ ]);
+ }
+
+ public function update(AdminInfoStoreRequest $request, Information $info)
+ {
+ $inputs = $request->all();
+ if (!$request->has('pinned')) {
+ $inputs['pinned'] = false;
+ }
+
+ $info->fill($inputs)->save();
+
+ return redirect()->route('admin.info.edit', ['info' => $info])->with('status', 'お知らせを更新しました。');
+ }
+
+ public function destroy(Information $info)
+ {
+ $info->delete();
+
+ return redirect()->route('admin.info')->with('status', 'お知らせを削除しました。');
+ }
+}
diff --git a/app/Http/Controllers/EjaculationController.php b/app/Http/Controllers/EjaculationController.php
index 4490677..d2292e0 100644
--- a/app/Http/Controllers/EjaculationController.php
+++ b/app/Http/Controllers/EjaculationController.php
@@ -30,9 +30,6 @@ class EjaculationController extends Controller
public function store(Request $request)
{
$inputs = $request->all();
- if ($request->has('note')) {
- $inputs['note'] = str_replace(["\r\n", "\r"], "\n", $inputs['note']);
- }
$validator = Validator::make($inputs, [
'date' => 'required|date_format:Y/m/d',
@@ -113,9 +110,6 @@ class EjaculationController extends Controller
$ejaculation = Ejaculation::findOrFail($id);
$inputs = $request->all();
- if ($request->has('note')) {
- $inputs['note'] = str_replace(["\r\n", "\r"], "\n", $inputs['note']);
- }
$validator = Validator::make($inputs, [
'date' => 'required|date_format:Y/m/d',
diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php
index 93bf68b..95714cf 100644
--- a/app/Http/Kernel.php
+++ b/app/Http/Kernel.php
@@ -35,6 +35,7 @@ class Kernel extends HttpKernel
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
+ \App\Http\Middleware\NormalizeLineEnding::class,
],
'api' => [
diff --git a/app/Http/Middleware/NormalizeLineEnding.php b/app/Http/Middleware/NormalizeLineEnding.php
new file mode 100644
index 0000000..e0ec87e
--- /dev/null
+++ b/app/Http/Middleware/NormalizeLineEnding.php
@@ -0,0 +1,30 @@
+input() as $key => $value) {
+ $newInput[$key] = str_replace(["\r\n", "\r"], "\n", $value);
+ }
+ $request->replace($newInput);
+
+ return $next($request);
+ }
+}
diff --git a/app/Http/Requests/AdminInfoStoreRequest.php b/app/Http/Requests/AdminInfoStoreRequest.php
new file mode 100644
index 0000000..45b3d2e
--- /dev/null
+++ b/app/Http/Requests/AdminInfoStoreRequest.php
@@ -0,0 +1,35 @@
+ ['required', Rule::in(array_keys(Information::CATEGORIES))],
+ 'pinned' => 'nullable|boolean',
+ 'title' => 'required|string|max:255',
+ 'content' => 'required|string|max:10000'
+ ];
+ }
+}
diff --git a/app/Information.php b/app/Information.php
index e0ae6ed..2b237fb 100644
--- a/app/Information.php
+++ b/app/Information.php
@@ -16,5 +16,9 @@ class Information extends Model
3 => ['label' => 'メンテナンス', 'class' => 'badge-warning']
];
+ protected $fillable = [
+ 'category', 'pinned', 'title', 'content'
+ ];
+
protected $dates = ['deleted_at'];
}
diff --git a/app/MetadataResolver/DLsiteResolver.php b/app/MetadataResolver/DLsiteResolver.php
index 99aae49..9f6d6e7 100644
--- a/app/MetadataResolver/DLsiteResolver.php
+++ b/app/MetadataResolver/DLsiteResolver.php
@@ -26,6 +26,17 @@ class DLsiteResolver implements Resolver
$res = $this->client->get($url);
if ($res->getStatusCode() === 200) {
$metadata = $this->ogpResolver->parse($res->getBody());
+
+ // 抽出
+ preg_match('~\[(.+)\] \| DLsite$~', $metadata->title, $match);
+ $maker = $match[1];
+
+ // 余分な文を消す
+ $metadata->title = trim(preg_replace('~ \[.+\] \| DLsite$~', '', $metadata->title));
+ $metadata->description = trim(preg_replace('~「DLsite.+」は同人誌・同人ゲーム・同人音声のダウンロードショップ。お気に入りの作品をすぐダウンロードできてすぐ楽しめる!毎日更新しているのであなたが探している作品にきっと出会えます。国内最大級の二次元総合ダウンロードショップ「DLsite」!$~', '', $metadata->description));
+
+ // 整形
+ $metadata->description = 'サークル: ' . $maker . PHP_EOL . $metadata->description;
$metadata->image = str_replace('img_sam.jpg', 'img_main.jpg', $metadata->image);
return $metadata;
diff --git a/app/MetadataResolver/FC2ContentsResolver.php b/app/MetadataResolver/FC2ContentsResolver.php
new file mode 100644
index 0000000..36a3b8b
--- /dev/null
+++ b/app/MetadataResolver/FC2ContentsResolver.php
@@ -0,0 +1,44 @@
+client = $client;
+ $this->ogpResolver = $ogpResolver;
+ }
+
+ public function resolve(string $url): Metadata
+ {
+ $res = $this->client->get($url);
+ if ($res->getStatusCode() === 200) {
+ $metadata = $this->ogpResolver->parse($res->getBody());
+
+ $dom = new \DOMDocument();
+ @$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8'));
+ $xpath = new \DOMXPath($dom);
+
+ $thumbnailNode = $xpath->query('//*[@class="main_thum_img"]/a')->item(0);
+ if ($thumbnailNode) {
+ $metadata->image = preg_replace('~^http:~', 'https:', $thumbnailNode->getAttribute('href'));
+ }
+
+ return $metadata;
+ } else {
+ throw new \RuntimeException("{$res->getStatusCode()}: $url");
+ }
+ }
+}
diff --git a/app/MetadataResolver/FanzaResolver.php b/app/MetadataResolver/FanzaResolver.php
index cfb73db..b1e1c66 100644
--- a/app/MetadataResolver/FanzaResolver.php
+++ b/app/MetadataResolver/FanzaResolver.php
@@ -27,6 +27,7 @@ class FanzaResolver implements Resolver
if ($res->getStatusCode() === 200) {
$metadata = $this->ogpResolver->parse($res->getBody());
$metadata->image = preg_replace("~(pr|ps)\.jpg$~", 'pl.jpg', $metadata->image);
+ $metadata->description = str_replace('<>', '', $metadata->description);
return $metadata;
} else {
diff --git a/app/MetadataResolver/MelonbooksResolver.php b/app/MetadataResolver/MelonbooksResolver.php
index 5cc3669..b94b719 100644
--- a/app/MetadataResolver/MelonbooksResolver.php
+++ b/app/MetadataResolver/MelonbooksResolver.php
@@ -30,11 +30,40 @@ class MelonbooksResolver implements Resolver
if ($res->getStatusCode() === 200) {
$metadata = $this->ogpResolver->parse($res->getBody());
+ $dom = new \DOMDocument();
+ @$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8'));
+ $xpath = new \DOMXPath($dom);
+ $descriptionNodelist = $xpath->query('//div[@id="description"]//p');
+ $specialDescriptionNodelist = $xpath->query('//div[@id="special_description"]//p');
+
// censoredフラグの除去
if (mb_strpos($metadata->image, '&c=1') !== false) {
$metadata->image = preg_replace('/&c=1/u', '', $metadata->image);
}
+ // 抽出
+ preg_match('~^(.+)((.+))の通販・購入はメロンブックス$~', $metadata->title, $match);
+ $title = $match[1];
+ $maker = $match[2];
+
+ // 整形
+ $description = 'サークル: ' . $maker . "\n";
+
+ if ($specialDescriptionNodelist->length !== 0) {
+ $description .= trim(str_replace('
', "\n", $specialDescriptionNodelist->item(0)->nodeValue)) . "\n";
+ if ($specialDescriptionNodelist->length === 2) {
+ $description .= "\n";
+ $description .= trim(str_replace('
', "\n", $specialDescriptionNodelist->item(1)->nodeValue)) . "\n";
+ }
+ }
+
+ if ($descriptionNodelist->length !== 0) {
+ $description .= trim(str_replace('
', "\n", $descriptionNodelist->item(0)->nodeValue));
+ }
+
+ $metadata->title = $title;
+ $metadata->description = trim($description);
+
return $metadata;
} else {
throw new \RuntimeException("{$res->getStatusCode()}: $url");
diff --git a/app/MetadataResolver/MetadataResolver.php b/app/MetadataResolver/MetadataResolver.php
index 5bf195f..181aab3 100644
--- a/app/MetadataResolver/MetadataResolver.php
+++ b/app/MetadataResolver/MetadataResolver.php
@@ -16,6 +16,7 @@ class MetadataResolver implements Resolver
'~ec\.toranoana\.jp/tora_r/ec/item/.*~' => ToranoanaResolver::class,
'~iwara\.tv/videos/.*~' => IwaraResolver::class,
'~www\.dlsite\.com/.*/work/=/product_id/..\d+\.html~' => DLsiteResolver::class,
+ '~dlsite\.jp/mawtw/..\d+~' => DLsiteResolver::class,
'~www\.pixiv\.net/member_illust\.php\?illust_id=\d+~' => PixivResolver::class,
'~fantia\.jp/posts/\d+~' => FantiaResolver::class,
'~dmm\.co\.jp/~' => FanzaResolver::class,
@@ -23,6 +24,8 @@ class MetadataResolver implements Resolver
'~www\.deviantart\.com/.*/art/.*~' => DeviantArtResolver::class,
'~\.syosetu\.com/n\d+[a-z]{2,}~' => NarouResolver::class,
'~ci-en\.jp/creator/\d+/article/\d+~' => CienResolver::class,
+ '~www\.plurk\.com\/p\/.*~' => PlurkResolver::class,
+ '~(adult\.)?contents\.fc2\.com\/article_search\.php\?id=\d+~' => FC2ContentsResolver::class,
];
public $mimeTypes = [
diff --git a/app/MetadataResolver/PatreonResolver.php b/app/MetadataResolver/PatreonResolver.php
index 5ce28e7..7ba3235 100644
--- a/app/MetadataResolver/PatreonResolver.php
+++ b/app/MetadataResolver/PatreonResolver.php
@@ -28,11 +28,11 @@ class PatreonResolver implements Resolver
if ($res->getStatusCode() === 200) {
$metadata = $this->ogpResolver->parse($res->getBody());
- parse_str(parse_url($metadata->image, PHP_URL_QUERY), $temp);
- $expires_at_unixtime = $temp['token-time'];
- $expires_at = Carbon::createFromTimestamp($expires_at_unixtime);
-
- $metadata->expires_at = $expires_at;
+ parse_str(parse_url($metadata->image, PHP_URL_QUERY), $query);
+ if (isset($query['token-time'])) {
+ $expires_at_unixtime = $query['token-time'];
+ $metadata->expires_at = Carbon::createFromTimestamp($expires_at_unixtime);
+ }
return $metadata;
} else {
diff --git a/app/MetadataResolver/PlurkResolver.php b/app/MetadataResolver/PlurkResolver.php
new file mode 100644
index 0000000..7422fef
--- /dev/null
+++ b/app/MetadataResolver/PlurkResolver.php
@@ -0,0 +1,44 @@
+client = $client;
+ $this->ogpResolver = $ogpResolver;
+ }
+
+ public function resolve(string $url): Metadata
+ {
+ $res = $this->client->get($url);
+ if ($res->getStatusCode() === 200) {
+ $metadata = $this->ogpResolver->parse($res->getBody());
+
+ $dom = new \DOMDocument();
+ @$dom->loadHTML(mb_convert_encoding($res->getBody(), 'HTML-ENTITIES', 'UTF-8'));
+ $xpath = new \DOMXPath($dom);
+ $imageNode = $xpath->query('//div[@class="text_holder"]/a[1]')->item(0);
+
+ if ($imageNode) {
+ $metadata->image = $imageNode->getAttribute('href');
+ }
+
+ return $metadata;
+ } else {
+ throw new \RuntimeException("{$res->getStatusCode()}: $url");
+ }
+ }
+}
diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php
index e12ff88..600ff72 100644
--- a/app/Providers/AuthServiceProvider.php
+++ b/app/Providers/AuthServiceProvider.php
@@ -25,6 +25,8 @@ class AuthServiceProvider extends ServiceProvider
{
$this->registerPolicies();
- //
+ Gate::define('admin', function ($user) {
+ return $user->is_admin;
+ });
}
}
diff --git a/database/migrations/2019_02_11_140657_add_is_admin_to_users.php b/database/migrations/2019_02_11_140657_add_is_admin_to_users.php
new file mode 100644
index 0000000..2b37986
--- /dev/null
+++ b/database/migrations/2019_02_11_140657_add_is_admin_to_users.php
@@ -0,0 +1,32 @@
+boolean('is_admin')->default(false);
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ *
+ * @return void
+ */
+ public function down()
+ {
+ Schema::table('users', function (Blueprint $table) {
+ $table->dropColumn('is_admin');
+ });
+ }
+}
diff --git a/dist/bin/tissue-entrypoint.sh b/dist/bin/tissue-entrypoint.sh
index 49280f5..5878f2f 100755
--- a/dist/bin/tissue-entrypoint.sh
+++ b/dist/bin/tissue-entrypoint.sh
@@ -3,6 +3,15 @@ set -e
if [[ "$APP_DEBUG" == "true" ]]; then
export PHP_INI_SCAN_DIR=":/usr/local/etc/php/php.d"
+
+ php -r "if (gethostbyname('host.docker.internal') === 'host.docker.internal') exit(1);" &> /dev/null && :
+ if [[ $? -eq 0 ]]; then
+ # Docker for Windows/Mac
+ export PHP_XDEBUG_REMOTE_HOST='host.docker.internal'
+ else
+ # Docker for Linux
+ export PHP_XDEBUG_REMOTE_HOST=$(cat /etc/hosts | awk 'END{print $1}' | sed -r -e 's/[0-9]+$/1/g')
+ fi
fi
exec docker-php-entrypoint "$@"
diff --git a/dist/php.d/99-xdebug.ini b/dist/php.d/99-xdebug.ini
index 007daa1..f2a7c9d 100644
--- a/dist/php.d/99-xdebug.ini
+++ b/dist/php.d/99-xdebug.ini
@@ -1,4 +1,4 @@
; Dockerでのデバッグ用設定
zend_extension=xdebug.so
xdebug.remote_enable=true
-xdebug.remote_host=host.docker.internal
\ No newline at end of file
+xdebug.remote_host=${PHP_XDEBUG_REMOTE_HOST}
\ No newline at end of file
diff --git a/package.json b/package.json
index c43f1b0..01fce83 100644
--- a/package.json
+++ b/package.json
@@ -7,21 +7,38 @@
"watch-poll": "npm run watch -- --watch-poll",
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
"prod": "npm run production",
- "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
+ "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
+ "stylelint": "stylelint resources/assets/sass/**/*"
},
"devDependencies": {
- "bootstrap": "^4.2.1",
+ "bootstrap": "^4.3.1",
"cal-heatmap": "^3.3.10",
"chart.js": "^2.7.1",
"cross-env": "^5.2.0",
+ "husky": "^1.3.1",
"jquery": "^3.2.1",
"js-cookie": "^2.2.0",
"laravel-mix": "^4.0.0",
+ "laravel-mix-bundle-analyzer": "^1.0.2",
+ "lint-staged": "^8.1.5",
"open-iconic": "^1.1.1",
"popper.js": "^1.14.7",
"resolve-url-loader": "^2.3.1",
"sass": "^1.17.0",
"sass-loader": "^7.1.0",
+ "stylelint": "^9.10.1",
+ "stylelint-config-recess-order": "^2.0.1",
"vue-template-compiler": "^2.6.6"
+ },
+ "stylelint": {
+ "extends": "stylelint-config-recess-order"
+ },
+ "husky": {
+ "hooks": {
+ "pre-commit": "lint-staged"
+ }
+ },
+ "lint-staged": {
+ "*.{css,scss}": ["stylelint --fix", "git add"]
}
}
diff --git a/public/dashboard.png b/public/dashboard.png
new file mode 100644
index 0000000..c85ca1c
Binary files /dev/null and b/public/dashboard.png differ
diff --git a/resources/assets/js/app.js b/resources/assets/js/app.js
index 0af3853..6af9993 100644
--- a/resources/assets/js/app.js
+++ b/resources/assets/js/app.js
@@ -21,12 +21,10 @@ $(() => {
$('.alert').alert();
$('.tis-page-selector').pageSelector();
- if (document.getElementById('status')) {
- setTimeout(function () {
- $('#status').alert('close');
- }, 5000);
- }
-
$('.link-card').linkCard();
- $('#deleteCheckinModal').deleteCheckinModal();
+ const $deleteCheckinModal = $('#deleteCheckinModal').deleteCheckinModal();
+ $(document).on('click', '[data-target="#deleteCheckinModal"]', function (event) {
+ event.preventDefault();
+ $deleteCheckinModal.modal('show', this);
+ });
});
\ No newline at end of file
diff --git a/resources/assets/sass/_bootstrap-custom.scss b/resources/assets/sass/_bootstrap-custom.scss
new file mode 100644
index 0000000..bafdd03
--- /dev/null
+++ b/resources/assets/sass/_bootstrap-custom.scss
@@ -0,0 +1,19 @@
+.card-img-left {
+ width: 100%;
+ @include border-left-radius($card-inner-border-radius);
+}
+
+.card-img-right {
+ width: 100%;
+ @include border-right-radius($card-inner-border-radius);
+}
+
+.card-img-top-to-left {
+ width: 100%;
+ @include media-breakpoint-down(md) {
+ @include border-top-radius($card-inner-border-radius);
+ }
+ @include media-breakpoint-up(lg) {
+ @include border-left-radius($card-inner-border-radius);
+ }
+}
\ No newline at end of file
diff --git a/resources/assets/sass/app.scss b/resources/assets/sass/app.scss
index 42fa070..e691a70 100644
--- a/resources/assets/sass/app.scss
+++ b/resources/assets/sass/app.scss
@@ -1,8 +1,15 @@
+// Bootstrap Variable Overlide
+$primary: #e53fb1;
+
// Bootstrap
@import "~bootstrap/scss/bootstrap";
+@import "bootstrap-custom";
// Open Iconic
@import "~open-iconic/font/css/open-iconic-bootstrap";
// Legacy app styles
-@import "tissue.css";
\ No newline at end of file
+@import "tissue.css";
+
+// Components
+@import "components/link-card";
\ No newline at end of file
diff --git a/resources/assets/sass/components/_link-card.scss b/resources/assets/sass/components/_link-card.scss
new file mode 100644
index 0000000..846fde9
--- /dev/null
+++ b/resources/assets/sass/components/_link-card.scss
@@ -0,0 +1,21 @@
+.link-card {
+ .row > div:last-child {
+ max-height: 400px;
+ overflow: hidden;
+
+ // 省略を表す影を付けるやつ
+ &::before {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ content: '';
+ background: linear-gradient(transparent 320px, white);
+ }
+ }
+
+ .card-text {
+ white-space: pre-line;
+ }
+}
\ No newline at end of file
diff --git a/resources/assets/sass/tissue.css b/resources/assets/sass/tissue.css
index f7dfd0c..2dd99e2 100644
--- a/resources/assets/sass/tissue.css
+++ b/resources/assets/sass/tissue.css
@@ -1,14 +1,10 @@
@charset "UTF-8";
.tis-footer {
- color: #a2a2a2;
font-size: small;
- border-top: 1px solid #eee;
- background: linear-gradient(to bottom, #f8f9fa, #fff)
-}
-
-.tis-word-wrap {
- word-wrap: break-word;
+ color: #a2a2a2;
+ background: linear-gradient(to bottom, #f8f9fa, #fff);
+ border-top: 1px solid #eee
}
.tis-contribution-graph {
@@ -16,8 +12,8 @@
}
.tis-need-agecheck .container {
- filter: blur(45px);
pointer-events: none;
+ filter: blur(45px);
}
.container {
@@ -25,8 +21,8 @@
}
.list-group-item.no-side-border {
- border-left: none;
border-right: none;
+ border-left: none;
border-radius: 0;
}
@@ -35,8 +31,8 @@
}
.list-group-item.border-bottom-only {
- border-left: none;
border-right: none;
+ border-left: none;
border-radius: 0;
}
@@ -54,12 +50,12 @@
}
.tis-page-selector {
- margin-left: -1px;
width: calc(100% + 2px);
height: 100%;
+ margin-left: -1px;
+ line-height: 1.25;
border: 1px solid #dee2e6;
border-radius: 0;
- line-height: 1.25;
}
@media (min-width: 992px) {
@@ -69,13 +65,13 @@
}
#navbarNav > .d-lg-none > .row > div:first-of-type {
- padding-left: 15px;
padding-right: 7.5px;
+ padding-left: 15px;
}
#navbarNav > .d-lg-none > .row > div {
- padding-left: 7.5px;
padding-right: 15px;
+ padding-left: 7.5px;
}
#navbarNav > .d-lg-none > .row > .col .btn {
@@ -84,16 +80,4 @@
#navbarAccountDropdownSp {
max-width: calc(100vw - 5em);
-}
-
-.card-img-left {
- width: 100%;
- border-top-left-radius: calc(.25rem - 1px);
- border-bottom-left-radius: calc(.25rem - 1px);
-}
-
-.card-img-right {
- width: 100%;
- border-top-right-radius: calc(.25rem - 1px);
- border-bottom-right-radius: calc(.25rem - 1px);
}
\ No newline at end of file
diff --git a/resources/lang/ja/validation.php b/resources/lang/ja/validation.php
index ecf3a03..d154816 100644
--- a/resources/lang/ja/validation.php
+++ b/resources/lang/ja/validation.php
@@ -119,6 +119,8 @@ return [
'attributes' => [
'email' => 'メールアドレス',
'password' => 'パスワード',
+ 'title' => 'タイトル',
+ 'content' => '本文',
],
];
diff --git a/resources/views/admin/dashboard.blade.php b/resources/views/admin/dashboard.blade.php
new file mode 100644
index 0000000..beb9602
--- /dev/null
+++ b/resources/views/admin/dashboard.blade.php
@@ -0,0 +1,10 @@
+@extends('layouts.admin')
+
+@section('title', 'ダッシュボード')
+
+@section('tab-content')
+
TODO: 役に立つ情報を表示する
+カテゴリ | +タイトル | +作成日 | +
---|---|---|
+ @if ($info->pinned) + ピン留め + @endif + {{ $categories[$info->category]['label'] }} + | ++ {{ $info->title }} + | ++ {{ $info->created_at->format('Y年n月j日') }} + | +
+
{!! Formatter::linkify(nl2br(e($ejaculation->note))) !!}
@endif diff --git a/resources/views/home.blade.php b/resources/views/home.blade.php index 9bf2128..34128ce 100644 --- a/resources/views/home.blade.php +++ b/resources/views/home.blade.php @@ -54,7 +54,7 @@最近の公開チェックインから、オカズリンク付きのものを表示しています。
+
{!! Formatter::linkify(nl2br(e($ejaculation->note))) !!}
@endif @endforeach