53 Commits

Author SHA1 Message Date
Irie Aoi
5c0aea50d7 タグ入力欄からフォーカスが外れた時文字があるならタグとして確定させる 2020-12-12 19:31:51 +09:00
dependabot[bot]
7bcf48c8bf Bump nunomaduro/collision from 3.0.1 to 3.1.0 (#565) 2020-12-08 13:46:31 +00:00
dependabot[bot]
8f1a94b863 Bump dot-prop from 4.2.0 to 4.2.1 (#561) 2020-12-08 13:08:25 +00:00
dependabot[bot]
1b54af63ce Bump facade/ignition from 1.16.3 to 1.16.4 (#567) 2020-12-08 13:04:43 +00:00
dependabot[bot]
3ba946ec11 Bump symfony/dom-crawler from 4.4.11 to 4.4.17 (#556) 2020-12-08 13:02:49 +00:00
dependabot[bot]
32e25a9f7a Bump laravel/helpers from 1.2.0 to 1.4.0 (#548) 2020-12-07 23:33:42 +00:00
dependabot[bot]
31626f48d9 Bump barryvdh/laravel-ide-helper from 2.8.0 to 2.8.1 (#540) 2020-12-07 23:06:43 +00:00
dependabot[bot]
174d802edf Bump league/csv from 9.6.0 to 9.6.1 (#541) 2020-12-07 22:51:57 +00:00
shibafu
9c9db69662 Merge pull request #558 from eai04191/fix-cien-resolver
Fix Cien resolver
2020-12-07 08:33:14 +09:00
Irie Aoi
8c5fdfb7f8 未使用のfixtureを削除 2020-12-06 19:37:56 +09:00
Irie Aoi
c616800da1 投稿に使える画像があれば使う 2020-12-06 19:37:56 +09:00
Irie Aoi
6449094b78 JWTがついているときだけexpires_atを付与する 2020-12-06 19:37:56 +09:00
Irie Aoi
072b3a0910 Cien fixture 更新 2020-12-06 19:37:56 +09:00
shibafu
83d22b807f Merge pull request #559 from shikorism/fix/xdebug-first-aid
CIでカバレッジ取りに使用するXdebugのバージョンを落とす
2020-12-05 10:01:38 +09:00
shibafu
914d92e545 CIでカバレッジ取りに使用するXdebugのバージョンを落とす 2020-12-03 21:20:23 +09:00
shibafu
dcef270790 Merge pull request #554 from shikorism/fix/negative-margin-destroyer
ユーザーページでstatus alertを出した時に余白が無くて不恰好なのを修正
2020-11-21 15:50:23 +09:00
shibafu
5808ac58ee ユーザーページでstatus alertを出した時に余白が無くて不恰好なのを修正 2020-11-18 23:09:21 +09:00
shibafu
b60932b6c5 add config for heroku 2020-11-13 23:39:42 +09:00
shibafu
28520007e4 Merge pull request #553 from shikorism/feature/332-discard-elapsed-time
「前回チェックインからの経過時間を記録しない」チェックインオプションの追加
2020-11-12 00:32:17 +09:00
shibafu
63af49ba70 概況計算でdiscard elapsed timeを考慮 2020-11-08 23:41:51 +09:00
shibafu
3825228344 discard elapsed timeフラグの立ったチェックインの表示対応 2020-11-08 23:07:51 +09:00
shibafu
855011c624 add discard_elapsed_time option 2020-11-08 15:38:54 +09:00
shibafu
bb7b05435e add migration 2020-11-08 01:04:56 +09:00
shibafu
5c26e58c1d Merge pull request #550 from shikorism/fix/239-actual-ejaculated-span
チェックインスパンの計算手段をWindow関数から相関サブクエリに変更
2020-11-07 17:26:19 +09:00
shibafu
ddb11ee96c Merge pull request #547 from shikorism/feature/realtime-checkin
日時指定を省略してチェックインする機能
2020-11-07 17:18:09 +09:00
shibafu
38c7755757 チェックインスパンの計算手段をWindow関数から相関サブクエリに変更 2020-11-06 09:12:25 +09:00
shibafu
c0cfbe1bc4 日時指定を省略してチェックインする機能 2020-11-03 14:19:25 +09:00
shibafu
96bf90104e Merge pull request #545 from shikorism/composer2
thanks prestissimo, goodbye!
2020-10-31 01:26:46 +09:00
shibafu
2bcd050fed thanks prestissimo, goodbye! 2020-10-31 01:06:06 +09:00
shibafu
ea1483a90a Merge pull request #538 from shikorism/fix/degrade-profile-stats
プロフィールページの概況が消えていたのを修正
2020-10-26 08:58:09 +09:00
shibafu
7e7d0f80c1 プロフィールページの概況が消えていたのを修正 2020-10-26 00:22:06 +09:00
shibafu
32398fea73 Merge pull request #536 from shikorism/fix/degrade-show-checkin
チェックイン個別ページのユーザープロフィールが半分消えているのを修正
2020-10-25 17:30:22 +09:00
shibafu
076e7d5d0d チェックイン個別ページのユーザープロフィールが半分消えているのを修正 2020-10-25 17:22:04 +09:00
dependabot[bot]
41c7679423 Bump symfony/css-selector from 4.4.11 to 4.4.15 (#523) 2020-10-25 07:55:53 +00:00
dependabot[bot]
486e5bad0a Bump barryvdh/laravel-debugbar from 3.4.1 to 3.5.1 (#516) 2020-10-25 07:47:00 +00:00
dependabot[bot]
a026897986 Bump laravel/framework from 6.18.35 to 6.19.1 (#533) 2020-10-25 07:36:53 +00:00
dependabot[bot]
96658ac34c Bump http-proxy from 1.18.0 to 1.18.1 (#514) 2020-10-25 07:32:42 +00:00
dependabot[bot]
622964ec01 Bump symfony/http-kernel from 4.4.11 to 4.4.13 (#510) 2020-10-25 07:05:32 +00:00
shibafu
141067beab Merge pull request #534 from shikorism/feature/refresh-navigations
プロフィールページ内タブのデザイン変更とヘッダーナビゲーションの再編
2020-10-25 16:04:21 +09:00
shibafu
61b83c18a7 ユーザー名のスタイル調整 2020-10-24 18:30:13 +09:00
shibafu
581ae56173 プロフィール内タブのデザイン調整 2020-10-24 15:40:11 +09:00
shibafu
b50a15c109 ヘッダーナビゲーションの内容を再編 2020-10-24 15:07:46 +09:00
shibafu
060bdeef43 ヘッダーメニューに自アカウントのグラフ・オカズへのリンクを追加 2020-10-24 15:00:21 +09:00
shibafu
be098c38cb ヘッダーのドロップダウンメニューの中身を別ファイルに分割 2020-10-24 14:53:12 +09:00
shibafu
d7d2bc2397 プロフィールページのタブ周りのデザインを刷新 2020-10-24 14:32:30 +09:00
shibafu
0f435c09b3 Merge pull request #479 from shikorism/feature/463-update-fixture
Fixture updater
2020-10-24 11:57:12 +09:00
shibafu
16071f7cff Merge pull request #531 from shikorism/fix/robots-ignore-non-plain-response
robots.txt取得時、text/plain以外の応答は失敗扱いにする
2020-10-21 20:47:19 +09:00
shibafu
a22f41766a remove unused use 2020-10-19 09:18:24 +09:00
shibafu
552ff421dd file_get_contentsを直接使うのをやめよう運動 2020-10-19 09:17:49 +09:00
shibafu
f8952474b5 MetadataResolverのFixtureを更新する仕組みを追加 2020-10-19 09:17:49 +09:00
shibafu
3fd62dcd6f robots.txt取得時、text/plain以外の応答は失敗扱いにする 2020-10-17 16:54:00 +09:00
shibafu
4360144d6f Merge pull request #530 from shikorism/fix/cien-jwt
Ci-en: JWTから有効期限を取得する
2020-10-17 16:20:18 +09:00
shibafu
e74a9675ce Ci-en: JWTから有効期限を取得する 2020-10-17 14:01:28 +09:00
51 changed files with 3148 additions and 1891 deletions

View File

@@ -24,6 +24,7 @@ commands:
- checkout
- run: sudo apt update
- run: sudo apt install -y libpq-dev
- run: sudo pecl install -f xdebug-2.9.8 && sudo docker-php-ext-enable xdebug
- run: sudo docker-php-ext-install zip
- run: sudo docker-php-ext-install pdo_pgsql
- run:

View File

@@ -9,6 +9,9 @@ LOG_CHANNEL=stack
# テストにモックを使用するか falseの場合は実際のHTML等を取得してテストする
TEST_USE_HTTP_MOCK=true
# テスト用のスナップショットを更新する場合はtrueにする (TEST_USE_HTTP_MOCKと重複させないよう注意)
TEST_UPDATE_SNAPSHOT=false
DB_CONNECTION=pgsql
DB_HOST=db
DB_PORT=5432

View File

@@ -10,7 +10,6 @@ RUN apt-get update \
&& pecl install xdebug \
&& curl -sS https://getcomposer.org/installer | php \
&& mv composer.phar /usr/local/bin/composer \
&& composer global require hirak/prestissimo \
&& sed -ri -e 's!/var/www/html!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/sites-available/*.conf \
&& sed -ri -e 's!/var/www/!${APACHE_DOCUMENT_ROOT}!g' /etc/apache2/apache2.conf /etc/apache2/conf-available/*.conf \
&& a2enmod rewrite

1
Procfile Normal file
View File

@@ -0,0 +1 @@
web: vendor/bin/heroku-php-apache2 public/

View File

@@ -19,7 +19,7 @@ class Ejaculation extends Model
protected $fillable = [
'user_id', 'ejaculated_date',
'note', 'geo_latitude', 'geo_longitude', 'link', 'source',
'is_private', 'is_too_sensitive',
'is_private', 'is_too_sensitive', 'discard_elapsed_time',
'checkin_webhook_id'
];
@@ -105,4 +105,33 @@ class Ejaculation extends Model
'is_too_sensitive' => $this->is_too_sensitive,
]);
}
public function ejaculatedSpan(): string
{
if (array_key_exists('ejaculated_span', $this->attributes)) {
if ($this->ejaculated_span === null) {
return '精通';
}
if ($this->discard_elapsed_time) {
return '0日 0時間 0分'; // TODO: 気の効いたフレーズにする
}
return $this->ejaculated_span;
} else {
$previous = Ejaculation::select('ejaculated_date')
->where('user_id', $this->user_id)
->where('ejaculated_date', '<', $this->ejaculated_date)
->orderByDesc('ejaculated_date')
->first();
if ($previous === null) {
return '精通';
}
if ($this->discard_elapsed_time) {
return '0日 0時間 0分';
}
return $this->ejaculated_date->diff($previous->ejaculated_date)->format('%a日 %h時間 %i分');
}
}
}

View File

@@ -35,6 +35,7 @@ class WebhookController extends Controller
'tags.*' => ['string', 'not_regex:/[\s\r\n]/u', 'max:255'],
'is_private' => 'nullable|boolean',
'is_too_sensitive' => 'nullable|boolean',
'discard_elapsed_time' => 'nullable|boolean',
], [
'tags.*.not_regex' => 'The :attribute cannot contain spaces, tabs and newlines.'
]);
@@ -71,6 +72,7 @@ class WebhookController extends Controller
'source' => Ejaculation::SOURCE_WEBHOOK,
'is_private' => (bool)($inputs['is_private'] ?? false),
'is_too_sensitive' => (bool)($inputs['is_too_sensitive'] ?? false),
'discard_elapsed_time' => (bool)($inputs['discard_elapsed_time'] ?? false),
'checkin_webhook_id' => $webhook->id
]);

View File

@@ -23,6 +23,7 @@ class EjaculationController extends Controller
$errors = $request->session()->get('errors');
$initialState = [
'mode' => 'create',
'fields' => [
'date' => old('date') ?? $request->input('date', date('Y/m/d')),
'time' => old('time') ?? $request->input('time', date('H:i')),
@@ -30,7 +31,9 @@ class EjaculationController extends Controller
'tags' => $tags,
'note' => old('note') ?? $request->input('note', ''),
'is_private' => old('is_private') ?? $request->input('is_private', 0) == 1,
'is_too_sensitive' => old('is_too_sensitive') ?? $request->input('is_too_sensitive', 0) == 1
'is_too_sensitive' => old('is_too_sensitive') ?? $request->input('is_too_sensitive', 0) == 1,
'is_realtime' => old('is_realtime', true),
'discard_elapsed_time' => old('discard_elapsed_time') ?? $request->input('discard_elapsed_time') == 1,
],
'errors' => isset($errors) ? $errors->getMessages() : null
];
@@ -43,15 +46,20 @@ class EjaculationController extends Controller
$inputs = $request->all();
$validator = Validator::make($inputs, [
'date' => 'required|date_format:Y/m/d',
'time' => 'required|date_format:H:i',
'date' => 'required_without:is_realtime|date_format:Y/m/d',
'time' => 'required_without:is_realtime|date_format:H:i',
'note' => 'nullable|string|max:500',
'link' => 'nullable|url|max:2000',
'tags' => 'nullable|string',
])->after(function ($validator) use ($request, $inputs) {
// 日時の重複チェック
if (!$validator->errors()->hasAny(['date', 'time'])) {
$dt = $inputs['date'] . ' ' . $inputs['time'];
if (isset($inputs['date']) && isset($inputs['time'])) {
$dt = Carbon::createFromFormat('Y/m/d H:i', $inputs['date'] . ' ' . $inputs['time']);
} else {
$dt = now();
}
$dt = $dt->startOfMinute();
if (Ejaculation::where(['user_id' => Auth::id(), 'ejaculated_date' => $dt])->count()) {
$validator->errors()->add('datetime', '既にこの日時にチェックインしているため、登録できません。');
}
@@ -59,18 +67,27 @@ class EjaculationController extends Controller
});
if ($validator->fails()) {
return redirect()->route('checkin')->withErrors($validator)->withInput();
return redirect()->route('checkin')
->withErrors($validator)
->withInput(array_merge(['is_realtime' => false], $request->input()));
}
$ejaculation = DB::transaction(function () use ($request, $inputs) {
if (isset($inputs['date']) && isset($inputs['time'])) {
$ejaculatedDate = Carbon::createFromFormat('Y/m/d H:i', $inputs['date'] . ' ' . $inputs['time']);
} else {
$ejaculatedDate = now();
}
$ejaculatedDate = $ejaculatedDate->startOfMinute();
$ejaculation = Ejaculation::create([
'user_id' => Auth::id(),
'ejaculated_date' => Carbon::createFromFormat('Y/m/d H:i', $inputs['date'] . ' ' . $inputs['time']),
'ejaculated_date' => $ejaculatedDate,
'note' => $inputs['note'] ?? '',
'link' => $inputs['link'] ?? '',
'source' => Ejaculation::SOURCE_WEB,
'is_private' => $request->has('is_private') ?? false,
'is_too_sensitive' => $request->has('is_too_sensitive') ?? false
'is_too_sensitive' => $request->has('is_too_sensitive') ?? false,
'discard_elapsed_time' => $request->has('discard_elapsed_time') ?? false,
]);
$tagIds = [];
@@ -104,21 +121,7 @@ class EjaculationController extends Controller
->firstOrFail();
$user = User::findOrFail($ejaculation->user_id);
// 1つ前のチェックインからの経過時間を求める
$previousEjaculation = Ejaculation::select('ejaculated_date')
->where('user_id', $ejaculation->user_id)
->where('ejaculated_date', '<', $ejaculation->ejaculated_date)
->orderByDesc('ejaculated_date')
->first();
if (!empty($previousEjaculation)) {
$ejaculatedSpan = $ejaculation->ejaculated_date
->diff($previousEjaculation->ejaculated_date)
->format('%a日 %h時間 %i分');
} else {
$ejaculatedSpan = null;
}
return view('ejaculation.show')->with(compact('user', 'ejaculation', 'ejaculatedSpan'));
return view('ejaculation.show')->with(compact('user', 'ejaculation'));
}
public function edit(Request $request, $id)
@@ -138,6 +141,7 @@ class EjaculationController extends Controller
$errors = $request->session()->get('errors');
$initialState = [
'mode' => 'update',
'fields' => [
'date' => old('date') ?? $ejaculation->ejaculated_date->format('Y/m/d'),
'time' => old('time') ?? $ejaculation->ejaculated_date->format('H:i'),
@@ -145,7 +149,8 @@ class EjaculationController extends Controller
'tags' => $tags,
'note' => old('note') ?? $ejaculation->note,
'is_private' => is_bool(old('is_private')) ? old('is_private') : $ejaculation->is_private,
'is_too_sensitive' => is_bool(old('is_too_sensitive')) ? old('is_too_sensitive') : $ejaculation->is_too_sensitive
'is_too_sensitive' => is_bool(old('is_too_sensitive')) ? old('is_too_sensitive') : $ejaculation->is_too_sensitive,
'discard_elapsed_time' => is_bool(old('discard_elapsed_time')) ? old('discard_elapsed_time') : $ejaculation->discard_elapsed_time,
],
'errors' => isset($errors) ? $errors->getMessages() : null
];
@@ -187,7 +192,8 @@ class EjaculationController extends Controller
'note' => $inputs['note'] ?? '',
'link' => $inputs['link'] ?? '',
'is_private' => $request->has('is_private') ?? false,
'is_too_sensitive' => $request->has('is_too_sensitive') ?? false
'is_too_sensitive' => $request->has('is_too_sensitive') ?? false,
'discard_elapsed_time' => $request->has('discard_elapsed_time') ?? false,
])->save();
$tagIds = [];

View File

@@ -28,17 +28,19 @@ class UserController extends Controller
// チェックインの取得
$query = Ejaculation::select(DB::raw(
<<<'SQL'
id,
ejaculations.id,
ejaculated_date,
note,
is_private,
is_too_sensitive,
link,
source,
to_char(lead(ejaculated_date, 1, NULL) OVER (ORDER BY ejaculated_date DESC), 'YYYY/MM/DD HH24:MI') AS before_date,
to_char(ejaculated_date - (lead(ejaculated_date, 1, NULL) OVER (ORDER BY ejaculated_date DESC)), 'FMDDD日 FMHH24時間 FMMI') AS ejaculated_span
discard_elapsed_time,
to_char(before_dates.before_date, 'YYYY/MM/DD HH24:MI') AS before_date,
to_char(ejaculated_date - before_dates.before_date, 'FMDDD日 FMHH24時間 FMMI分') AS ejaculated_span
SQL
))
->joinSub($this->queryBeforeEjaculatedDates(), 'before_dates', 'before_dates.id', '=', 'ejaculations.id')
->where('user_id', $user->id);
if (!Auth::check() || $user->id !== Auth::id()) {
$query = $query->where('is_private', false);
@@ -161,17 +163,19 @@ SQL
// チェックインの取得
$query = Ejaculation::select(DB::raw(
<<<'SQL'
id,
ejaculations.id,
ejaculated_date,
note,
is_private,
is_too_sensitive,
link,
source,
to_char(lead(ejaculated_date, 1, NULL) OVER (ORDER BY ejaculated_date DESC), 'YYYY/MM/DD HH24:MI') AS before_date,
to_char(ejaculated_date - (lead(ejaculated_date, 1, NULL) OVER (ORDER BY ejaculated_date DESC)), 'FMDDD日 FMHH24時間 FMMI') AS ejaculated_span
discard_elapsed_time,
to_char(before_dates.before_date, 'YYYY/MM/DD HH24:MI') AS before_date,
to_char(ejaculated_date - before_dates.before_date, 'FMDDD日 FMHH24時間 FMMI分') AS ejaculated_span
SQL
))
->joinSub($this->queryBeforeEjaculatedDates(), 'before_dates', 'before_dates.id', '=', 'ejaculations.id')
->where('user_id', $user->id)
->where('link', '<>', '');
if (!Auth::check() || $user->id !== Auth::id()) {
@@ -303,4 +307,14 @@ SQL
->groupBy(DB::raw("to_char(ejaculated_date, 'YYYY/MM/DD')"))
->orderBy(DB::raw("to_char(ejaculated_date, 'YYYY/MM/DD')"));
}
private function queryBeforeEjaculatedDates()
{
return DB::table('ejaculations')->selectRaw(
<<<'SQL'
id,
(select ejaculated_date from ejaculations e2 where e2.ejaculated_date < ejaculations.ejaculated_date and e2.user_id = ejaculations.user_id order by e2.ejaculated_date desc limit 1) AS before_date
SQL
);
}
}

View File

@@ -19,6 +19,7 @@ class ProfileStatsComposer
if (!$view->offsetExists('user')) {
throw new \LogicException('View data "user" was not exist.');
}
/** @var \App\User $user */
$user = $view->offsetGet('user');
// 現在のオナ禁セッションの経過時間
@@ -35,35 +36,44 @@ class ProfileStatsComposer
}
// 概況欄のデータ取得
$average = DB::select(<<<'SQL'
$average = 0;
$divisor = 0;
$averageSources = DB::select(<<<'SQL'
SELECT
avg(span) AS average
extract(epoch from ejaculated_date - lead(ejaculated_date, 1, NULL) OVER (ORDER BY ejaculated_date DESC)) AS span,
discard_elapsed_time
FROM
(
SELECT
extract(epoch from ejaculated_date - lead(ejaculated_date, 1, NULL) OVER (ORDER BY ejaculated_date DESC)) AS span
FROM
ejaculations
WHERE
user_id = :user_id
ORDER BY
ejaculated_date DESC
LIMIT
30
) AS temp
ejaculations
WHERE
user_id = :user_id
ORDER BY
ejaculated_date DESC
LIMIT
30
SQL
, ['user_id' => $user->id]);
foreach ($averageSources as $item) {
// 経過時間記録対象外のレコードがあったら、それより古いデータは平均の計算に加えない
if ($item->discard_elapsed_time) {
break;
}
$average += $item->span;
$divisor++;
}
if ($divisor > 0) {
$average /= $divisor;
}
$summary = DB::select(<<<'SQL'
SELECT
max(span) AS longest,
min(span) AS shortest,
sum(span) AS total_times,
count(*) AS total_checkins
sum(span) AS total_times
FROM
(
SELECT
extract(epoch from ejaculated_date - lead(ejaculated_date, 1, NULL) OVER (ORDER BY ejaculated_date DESC)) AS span
extract(epoch from ejaculated_date - lead(ejaculated_date, 1, NULL) OVER (ORDER BY ejaculated_date DESC)) AS span,
discard_elapsed_time
FROM
ejaculations
WHERE
@@ -71,9 +81,13 @@ FROM
ORDER BY
ejaculated_date DESC
) AS temp
WHERE
discard_elapsed_time = FALSE
SQL
, ['user_id' => $user->id]);
$view->with(compact('latestEjaculation', 'currentSession', 'average', 'summary'));
$total = $user->ejaculations()->count();
$view->with(compact('latestEjaculation', 'currentSession', 'average', 'summary', 'total'));
}
}

View File

@@ -4,6 +4,7 @@ namespace App\MetadataResolver;
use Carbon\Carbon;
use GuzzleHttp\Client;
use Symfony\Component\DomCrawler\Crawler;
class CienResolver extends MetadataResolver
{
@@ -25,14 +26,27 @@ class CienResolver extends MetadataResolver
public function resolve(string $url): Metadata
{
$res = $this->client->get($url);
$metadata = $this->ogpResolver->parse((string) $res->getBody());
$html = (string) $res->getBody();
$metadata = $this->ogpResolver->parse($html);
$crawler = new Crawler($html);
// 画像URLから有効期限の起点を拾
parse_str(parse_url($metadata->image, PHP_URL_QUERY), $params);
if (empty($params['px-time'])) {
throw new \RuntimeException('Parameter "px-time" not found. Image=' . $metadata->image . ' Source=' . $url);
// OGPのデフォルトはバナーなので、投稿に使える画像があればそれを使
$selector = 'img[data-actual*="image-web"]';
if ($crawler->filter($selector)->count() !== 0) {
$metadata->image = $crawler->filter($selector)->attr('data-actual');
}
// JWTがついていれば画像URLのJWTから有効期限を拾う
parse_str(parse_url($metadata->image, PHP_URL_QUERY), $params);
if (isset($params['jwt'])) {
$parts = explode('.', $params['jwt']);
if (count($parts) !== 3) {
throw new \RuntimeException('Invalid jwt. Image=' . $metadata->image . ' Source=' . $url);
}
$payload = json_decode(base64_decode(str_replace(['-', '_'], ['+', '/'], $parts[1])), true);
$metadata->expires_at = Carbon::createFromTimestamp($payload['exp']);
}
$metadata->expires_at = Carbon::createFromTimestamp($params['px-time'])->addHour(1);
return $metadata;
}

View File

@@ -162,6 +162,11 @@ class MetadataResolveService
$client = app(Client::class);
try {
$res = $client->get($robotsUrl);
if (stripos($res->getHeaderLine('Content-Type'), 'text/plain') !== 0) {
Log::error('robots.txtの取得に失敗: 不適切なContent-Type (' . $res->getHeaderLine('Content-Type') . ')');
return null;
}
return (string) $res->getBody();
} catch (\Exception $e) {

978
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddDiscardElapsedTimeToEjaculations extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('ejaculations', function (Blueprint $table) {
$table->boolean('discard_elapsed_time')->default(false);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('ejaculations', function (Blueprint $table) {
$table->dropColumn('discard_elapsed_time');
});
}
}

View File

@@ -4,7 +4,7 @@ info:
description: |
夜のライフログサービス Tissue の公開API仕様です。
全てのAPIのURLは `https://shikorism.net/api` から始まります。
version: 0.1.0
version: 0.1.1
servers:
- url: 'https://shikorism.net/api'
paths:
@@ -51,6 +51,10 @@ paths:
type: boolean
default: false
description: チェックイン対象のオカズをより過激なオカズとして設定
discard_elapsed_time:
type: boolean
default: false
description: 前回チェックインからの経過時間を記録しない
examples:
simple:
description: 何も指定しなければ、現在時刻で公開チェックインをおこないます。

View File

@@ -10,7 +10,8 @@
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"eslint": "eslint --ext .js,.ts,.tsx resources/",
"stylelint": "stylelint resources/assets/sass/**/*",
"doc": "redoc-cli bundle -o public/apidoc.html openapi.yaml"
"doc": "redoc-cli bundle -o public/apidoc.html openapi.yaml",
"heroku-postbuild": "npm run production && npm run doc"
},
"devDependencies": {
"@types/bootstrap": "^4.5.0",

View File

@@ -1,5 +1,6 @@
import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import classNames from 'classnames';
import { format } from 'date-fns';
import { CheckBox } from './CheckBox';
import { FieldError, StandaloneFieldError } from './FieldError';
import { TagInput } from './TagInput';
@@ -10,18 +11,43 @@ type CheckinFormProps = {
};
export const CheckinForm: React.FC<CheckinFormProps> = ({ initialState }) => {
const mode = initialState.mode;
const [date, setDate] = useState<string>(initialState.fields.date || '');
const [time, setTime] = useState<string>(initialState.fields.time || '');
const [tags, setTags] = useState<string[]>(initialState.fields.tags || []);
const [link, setLink] = useState<string>(initialState.fields.link || '');
const [linkForPreview, setLinkForPreview] = useState(link);
const [note, setNote] = useState<string>(initialState.fields.note || '');
const [isRealtime, setRealtime] = useState<boolean>(mode === 'create' && initialState.fields.is_realtime);
const [isPrivate, setPrivate] = useState<boolean>(!!initialState.fields.is_private);
const [isTooSensitive, setTooSensitive] = useState<boolean>(!!initialState.fields.is_too_sensitive);
const [discardElapsedTime, setDiscardElapsedTime] = useState<boolean>(!!initialState.fields.discard_elapsed_time);
useEffect(() => {
if (mode === 'create' && isRealtime) {
const id = setInterval(() => {
const now = new Date();
setDate(format(now, 'yyyy/MM/dd'));
setTime(format(now, 'HH:mm'));
}, 500);
return () => clearInterval(id);
}
}, [mode, isRealtime]);
return (
<>
<div className="form-row">
{mode === 'create' && (
<div className="col-sm-12 mb-2">
<CheckBox
id="isRealtime"
name="is_realtime"
checked={isRealtime}
onChange={(v) => setRealtime(v)}
>
</CheckBox>
</div>
)}
<div className="form-group col-sm-6">
<label htmlFor="date">
<span className="oi oi-calendar" />
@@ -38,6 +64,7 @@ export const CheckinForm: React.FC<CheckinFormProps> = ({ initialState }) => {
required
value={date}
onChange={(e) => setDate(e.target.value)}
disabled={isRealtime}
/>
<FieldError errors={initialState.errors?.date} />
</div>
@@ -57,6 +84,7 @@ export const CheckinForm: React.FC<CheckinFormProps> = ({ initialState }) => {
required
value={time}
onChange={(e) => setTime(e.target.value)}
disabled={isRealtime}
/>
<FieldError errors={initialState.errors?.time} />
</div>
@@ -137,6 +165,23 @@ export const CheckinForm: React.FC<CheckinFormProps> = ({ initialState }) => {
>
<span className="oi oi-warning" />
</CheckBox>
<CheckBox
id="discardElapsedTime"
name="discard_elapsed_time"
className="mb-3"
checked={discardElapsedTime}
onChange={(v) => setDiscardElapsedTime(v)}
>
<span className="oi oi-timer" />
<br />
<small className="form-text text-muted">
使
<ul className="pl-3">
<li></li>
<li></li>
</ul>
</small>
</CheckBox>
</div>
</div>
<div className="text-center">

View File

@@ -23,8 +23,7 @@ export const TagInput: React.FC<TagInputProps> = ({ id, name, values, isInvalid,
case 'Enter':
case ' ':
if ((event as any).isComposing !== true) {
onChange && onChange(values.concat(buffer.trim().replace(/\s+/g, '_')));
setBuffer('');
commitBuffer();
}
event.preventDefault();
break;
@@ -32,8 +31,7 @@ export const TagInput: React.FC<TagInputProps> = ({ id, name, values, isInvalid,
// 実際にテキストボックスに入力されている文字を見に行く (フォールバック処理)
const nativeEvent = event.nativeEvent;
if (nativeEvent.srcElement && (nativeEvent.srcElement as HTMLInputElement).value.slice(-1) == ' ') {
onChange && onChange(values.concat(buffer.trim().replace(/\s+/g, '_')));
setBuffer('');
commitBuffer();
event.preventDefault();
}
break;
@@ -45,6 +43,13 @@ export const TagInput: React.FC<TagInputProps> = ({ id, name, values, isInvalid,
}
};
const commitBuffer = () => {
const newTag = buffer.trim().replace(/\s+/g, '_');
if (newTag.length === 0) return;
onChange && onChange(values.concat(newTag));
setBuffer('');
};
return (
<div className={containerClass} onClick={() => inputRef.current?.focus()}>
<input name={name} type="hidden" value={values.join(' ')} />
@@ -66,6 +71,7 @@ export const TagInput: React.FC<TagInputProps> = ({ id, name, values, isInvalid,
className="tis-tag-input-field"
value={buffer}
onChange={(e) => setBuffer(e.target.value)}
onBlur={commitBuffer}
onKeyDown={onKeyDown}
/>
</li>

View File

@@ -0,0 +1,24 @@
.tis-nav-underline-tabs {
margin-bottom: -1px;
.nav-link {
padding: 0.5rem 1.25rem;
color: $secondary;
border-bottom: 2px solid transparent;
transition: border-bottom-color .12s ease-in;
@include media-breakpoint-up(lg) {
padding: 1rem 1.25rem;
}
&.active {
color: $primary;
border-bottom-color: $primary;
}
&:not(.active):hover {
border-bottom-color: transparentize($secondary, 0.3);
transition: border-bottom-color .4s ease-out;
}
}
}

View File

@@ -15,6 +15,15 @@ $primary: #e53fb1;
@import "components/link-card";
@import "components/tag-input";
@import "components/metadata-preview";
@import "components/profile-mini";
// Tag
@import "tag/index";
// Underline tabs
@import "underline-tabs";
// Status containerの後続要素との余白調整
.tis-status-container + .mt-n1 {
margin-top: 0 !important;
}

View File

@@ -0,0 +1,11 @@
.tis-profile-mini {
&-display-name {
font-size: 1.2rem;
font-weight: 500;
}
&-name {
margin-top: 0.125rem;
font-size: 0.8rem;
}
}

View File

@@ -0,0 +1,17 @@
<a href="{{ route('user.profile', ['name' => Auth::user()->name]) }}" class="dropdown-item">
<strong>{{ Auth::user()->display_name }}</strong>
<p class="mb-0 text-muted">
<span>&commat;{{ Auth::user()->name }}</span>
</p>
</a>
<div class="dropdown-divider"></div>
<a href="{{ route('user.profile', ['name' => Auth::user()->name]) }}" class="dropdown-item">プロフィール</a>
<a href="{{ route('user.stats', ['name' => Auth::user()->name]) }}" class="dropdown-item">グラフ</a>
<a href="{{ route('user.okazu', ['name' => Auth::user()->name]) }}" class="dropdown-item">オカズ</a>
<a href="{{ route('user.likes', ['name' => Auth::user()->name]) }}" class="dropdown-item">いいね</a>
<div class="dropdown-divider"></div>
<a href="{{ route('setting') }}" class="dropdown-item">設定</a>
@can ('admin')
<a href="{{ route('admin.dashboard') }}" class="dropdown-item">管理</a>
@endcan
<a href="{{ route('logout') }}" class="dropdown-item" onclick="event.preventDefault(); document.getElementById('logout-form').submit();">ログアウト</a>

View File

@@ -0,0 +1,20 @@
@if (!empty($user->bio) || !empty($user->url))
<div class="card mb-4">
<div class="card-body">
{{-- Bio --}}
@if (!empty($user->bio))
<p class="card-text mb-0">
{!! Formatter::linkify(nl2br(e($user->bio))) !!}
</p>
@endif
{{-- URL --}}
@if (!empty($user->url))
<p class="card-text d-flex mt-3">
<span class="oi oi-link-intact mr-1 mt-1"></span>
<a href="{{ $user->url }}" rel="me nofollow noopener" target="_blank" class="text-truncate">{{ preg_replace('~\Ahttps?://~', '', $user->url) }}</a>
</p>
@endif
</div>
</div>
@endif

View File

@@ -1,14 +1,14 @@
<div class="d-flex flex-row align-items-end {{ $class ?? '' }}">
<img src="{{ $user->getProfileImageUrl(48) }}" srcset="{{ Formatter::profileImageSrcSet($user, 48) }}" class="rounded mr-2">
<div class="d-flex flex-column overflow-hidden">
<h5 class="card-title text-truncate">
<div class="tis-profile-mini-display-name text-truncate">
<a class="text-dark" href="{{ route('user.profile', ['name' => $user->name]) }}">{{ $user->display_name }}</a>
</h5>
<h6 class="card-subtitle">
</div>
<div class="tis-profile-mini-name">
<a class="text-muted" href="{{ route('user.profile', ['name' => $user->name]) }}">&commat;{{ $user->name }}</a>
@if ($user->is_protected)
<span class="oi oi-lock-locked text-muted"></span>
@endif
</h6>
</div>
</div>
</div>

View File

@@ -8,8 +8,8 @@
@endif
<h6 class="font-weight-bold"><span class="oi oi-graph"></span> 概況</h6>
<p class="card-text mb-0">平均記録: {{ Formatter::formatInterval($average[0]->average) }}</p>
<p class="card-text mb-0">平均記録: {{ Formatter::formatInterval($average) }}</p>
<p class="card-text mb-0">最長記録: {{ Formatter::formatInterval($summary[0]->longest) }}</p>
<p class="card-text mb-0">最短記録: {{ Formatter::formatInterval($summary[0]->shortest) }}</p>
<p class="card-text mb-0">合計時間: {{ Formatter::formatInterval($summary[0]->total_times) }}</p>
<p class="card-text">通算回数: {{ number_format($summary[0]->total_checkins) }}</p>
<p class="card-text">通算回数: {{ number_format($total) }}</p>

View File

@@ -5,7 +5,7 @@
<a class="text-dark" href="{{ route('user.profile', ['name' => $user->name]) }}">{{ $user->display_name }}</a>
</h4>
<h6 class="card-subtitle">
<a class="text-muted" href="{{ route('user.profile', ['name' => $user->name]) }}">&commat;{{ $user->name }}</a>
<a class="text-muted font-weight-normal" href="{{ route('user.profile', ['name' => $user->name]) }}">&commat;{{ $user->name }}</a>
@if ($user->is_protected)
<span class="oi oi-lock-locked text-muted"></span>
@endif

View File

@@ -31,7 +31,7 @@
<div class="card-body">
<!-- span -->
<div>
<h5>{{ $ejaculatedSpan ?? '精通' }} <small class="text-muted">{{ $ejaculation->before_date }}{{ !empty($ejaculation->before_date) ? ' ' : '' }}{{ $ejaculation->ejaculated_date->format('Y/m/d H:i') }}</small></h5>
<h5>{{ $ejaculation->ejaculatedSpan() }} <small class="text-muted">{{ !empty($ejaculation->before_date) && !$ejaculation->discard_elapsed_time ? $ejaculation->before_date . ' ' : '' }}{{ $ejaculation->ejaculated_date->format('Y/m/d H:i') }}</small></h5>
</div>
<!-- tags -->
@if ($ejaculation->is_private || $ejaculation->source !== 'web' || $ejaculation->tags->isNotEmpty())

View File

@@ -43,21 +43,7 @@
<img src="{{ Auth::user()->getProfileImageUrl(30) }}" srcset="{{ Formatter::profileImageSrcSet(Auth::user(), 30) }}" width="30" height="30" class="rounded d-inline-block align-top">
</a>
<div class="dropdown-menu dropdown-menu-right position-absolute" aria-labelledby="navbarDropdownMenuLink" id="navbarAccountDropdownSp">
<a href="{{ route('user.profile', ['name' => Auth::user()->name]) }}" class="dropdown-item text-truncate">
<strong>{{ Auth::user()->display_name }}</strong>
<p class="mb-0 text-muted">
<span>&commat;{{ Auth::user()->name }}</span>
</p>
</a>
<div class="dropdown-divider"></div>
<a href="{{ route('user.profile', ['name' => Auth::user()->name]) }}" class="dropdown-item">プロフィール</a>
<a href="{{ route('user.likes', ['name' => Auth::user()->name]) }}" class="dropdown-item">いいね</a>
<div class="dropdown-divider"></div>
<a href="{{ route('setting') }}" class="dropdown-item">設定</a>
@can ('admin')
<a href="{{ route('admin.dashboard') }}" class="dropdown-item">管理</a>
@endcan
<a href="{{ route('logout') }}" class="dropdown-item" onclick="event.preventDefault(); document.getElementById('logout-form').submit();">ログアウト</a>
@include('components.header-dropdown-menu')
</div>
</div>
</div>
@@ -73,14 +59,8 @@
<li class="nav-item {{ stripos(Route::currentRouteName(), 'home') === 0 ? 'active' : ''}}">
<a class="nav-link" href="{{ route('home') }}">ホーム</a>
</li>
<li class="nav-item {{ stripos(Route::currentRouteName(), 'user.profile') === 0 ? 'active' : ''}}">
<a class="nav-link" href="{{ route('user.profile', ['name' => Auth::user()->name]) }}">タイムライン</a>
</li>
<li class="nav-item {{ stripos(Route::currentRouteName(), 'user.stats') === 0 ? 'active' : ''}}">
<a class="nav-link" href="{{ route('user.stats', ['name' => Auth::user()->name]) }}">グラフ</a>
</li>
<li class="nav-item {{ stripos(Route::currentRouteName(), 'user.okazu') === 0 ? 'active' : ''}}">
<a class="nav-link" href="{{ route('user.okazu', ['name' => Auth::user()->name]) }}">オカズ</a>
<li class="nav-item {{ stripos(Route::currentRouteName(), 'timeline.public') === 0 ? 'active' : ''}}">
<a class="nav-link" href="{{ route('timeline.public') }}">お惣菜</a>
</li>
<li class="nav-item {{ stripos(Route::currentRouteName(), 'tag') === 0 ? 'active' : ''}}">
<a class="nav-link" href="{{ route('tag') }}">タグ一覧</a>
@@ -106,21 +86,7 @@
<img src="{{ Auth::user()->getProfileImageUrl(30) }}" srcset="{{ Formatter::profileImageSrcSet(Auth::user(), 30) }}" width="30" height="30" class="rounded d-inline-block align-top">
</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdownMenuLink">
<a href="{{ route('user.profile', ['name' => Auth::user()->name]) }}" class="dropdown-item">
<strong>{{ Auth::user()->display_name }}</strong>
<p class="mb-0 text-muted">
<span>&commat;{{ Auth::user()->name }}</span>
</p>
</a>
<div class="dropdown-divider"></div>
<a href="{{ route('user.profile', ['name' => Auth::user()->name]) }}" class="dropdown-item">プロフィール</a>
<a href="{{ route('user.likes', ['name' => Auth::user()->name]) }}" class="dropdown-item">いいね</a>
<div class="dropdown-divider"></div>
<a href="{{ route('setting') }}" class="dropdown-item">設定</a>
@can ('admin')
<a href="{{ route('admin.dashboard') }}" class="dropdown-item">管理</a>
@endcan
<a href="{{ route('logout') }}" class="dropdown-item" onclick="event.preventDefault(); document.getElementById('logout-form').submit();">ログアウト</a>
@include('components.header-dropdown-menu')
</div>
</li>
</ul>
@@ -132,15 +98,7 @@
<a class="btn btn-{{ stripos(Route::currentRouteName(), 'home') === 0 ? 'primary' : 'outline-secondary'}}" href="{{ route('home') }}" role="button">ホーム</a>
</div>
<div class="col">
<a class="btn btn-{{ stripos(Route::currentRouteName(), 'user.profile') === 0 ? 'primary' : 'outline-secondary'}}" href="{{ route('user.profile', ['name' => Auth::user()->name]) }}" role="button">タイムライン</a>
</div>
</div>
<div class="row mt-2">
<div class="col">
<a class="btn btn-{{ stripos(Route::currentRouteName(), 'user.stats') === 0 ? 'primary' : 'outline-secondary'}}" href="{{ route('user.stats', ['name' => Auth::user()->name]) }}" role="button">グラフ</a>
</div>
<div class="col">
<a class="btn btn-{{ stripos(Route::currentRouteName(), 'user.okazu') === 0 ? 'primary' : 'outline-secondary'}}" href="{{ route('user.okazu', ['name' => Auth::user()->name]) }}" role="button">オカズ</a>
<a class="btn btn-{{ stripos(Route::currentRouteName(), 'timeline.public') === 0 ? 'primary' : 'outline-secondary'}}" href="{{ route('timeline.public') }}" role="button">お惣菜</a>
</div>
</div>
<div class="row mt-2">
@@ -204,7 +162,7 @@
</div>
</nav>
@if (session('status'))
<div class="container">
<div class="container tis-status-container">
<div id="status" class="alert alert-success alert-dismissible fade show" role="alert">
{{ session('status') }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">

View File

@@ -1,46 +1,45 @@
@extends('layouts.base')
@section('content')
<div class="container">
<div class="row">
<div class="col-lg-4">
@if (Route::currentRouteName() === 'user.profile')
@component('components.profile', ['user' => $user])
<div class="container-fluid border-bottom mb-4 mt-n1 mt-lg-n4 px-0">
<div class="container">
<div class="row align-items-center">
<div class="col-lg-4">
@component('components.profile-mini', ['user' => $user])
@endcomponent
@else
<div class="card mb-4">
<div class="card-body">
@component('components.profile-mini', ['user' => $user])
@endcomponent
</div>
</div>
@endif
@section('sidebar')
@show
</div>
<div class="col-lg-8">
<ul class="nav nav-tabs">
<li class="nav-item">
<a class="nav-link {{ Route::currentRouteName() === 'user.profile' ? 'active' : '' }}" href="{{ route('user.profile', ['name' => $user->name]) }}">タイムライン</a>
</li>
<li class="nav-item">
<a class="nav-link {{ stripos(Route::currentRouteName(), 'user.stats') === 0 ? 'active' : '' }}" href="{{ route('user.stats', ['name' => $user->name]) }}">グラフ</a>
</li>
<li class="nav-item">
<a class="nav-link {{ Route::currentRouteName() === 'user.okazu' ? 'active' : '' }}" href="{{ route('user.okazu', ['name' => $user->name]) }}">オカズ</a>
</li>
<li class="nav-item">
<a class="nav-link {{ Route::currentRouteName() === 'user.likes' ? 'active' : '' }}" href="{{ route('user.likes', ['name' => $user->name]) }}">いいね
@if ($user->isMe() || !($user->is_protected || $user->private_likes))
<span class="badge badge-primary">{{ $user->likes()->count() }}</span>
@endif
</a>
</li>
</ul>
<div class="tab-content">
@yield('tab-content')
</div>
<div class="col-lg-8 mt-3 mt-lg-2 px-0 px-md-2">
<ul class="nav tis-nav-underline-tabs flex-nowrap overflow-auto">
<li class="nav-item flex-shrink-0">
<a class="nav-link {{ Route::currentRouteName() === 'user.profile' ? 'active' : '' }}" href="{{ route('user.profile', ['name' => $user->name]) }}">タイムライン</a>
</li>
<li class="nav-item flex-shrink-0">
<a class="nav-link {{ stripos(Route::currentRouteName(), 'user.stats') === 0 ? 'active' : '' }}" href="{{ route('user.stats', ['name' => $user->name]) }}">グラフ</a>
</li>
<li class="nav-item flex-shrink-0">
<a class="nav-link {{ Route::currentRouteName() === 'user.okazu' ? 'active' : '' }}" href="{{ route('user.okazu', ['name' => $user->name]) }}">オカズ</a>
</li>
<li class="nav-item flex-shrink-0">
<a class="nav-link {{ Route::currentRouteName() === 'user.likes' ? 'active' : '' }}" href="{{ route('user.likes', ['name' => $user->name]) }}">いいね
@if ($user->isMe() || !($user->is_protected || $user->private_likes))
<span class="badge {{ Route::currentRouteName() === 'user.likes' ? 'badge-primary' : 'badge-secondary' }}">{{ $user->likes()->count() }}</span>
@endif
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-lg-4">
@section('sidebar')
@show
</div>
<div class="col-lg-8">
@yield('tab-content')
</div>
</div>
</div>
@endsection

View File

@@ -11,6 +11,16 @@
@section('sidebar')
{{-- TODO: タイムラインとオカズのテンプレを分けたら条件外す --}}
@if (Route::currentRouteName() === 'user.profile')
@component('components.profile-bio', ['user' => $user])
@endcomponent
@if (!$user->is_protected || $user->isMe())
<div class="card mb-4">
<div class="card-body">
@component('components.profile-stats', ['user' => $user])
@endcomponent
</div>
</div>
@endif
@if (!empty($tags) && (!$user->is_protected || $user->isMe()))
<div class="card mb-4">
<div class="card-header">
@@ -48,7 +58,7 @@
<li class="list-group-item border-bottom-only pt-3 pb-3 text-break">
<!-- span -->
<div>
<h5>{{ $ejaculation->ejaculated_span ?? '精通' }} <a href="{{ route('checkin.show', ['id' => $ejaculation->id]) }}" class="text-muted"><small>{{ $ejaculation->before_date }}{{ !empty($ejaculation->before_date) ? ' ' : '' }}{{ $ejaculation->ejaculated_date->format('Y/m/d H:i') }}</small></a></h5>
<h5>{{ $ejaculation->ejaculatedSpan() }} <a href="{{ route('checkin.show', ['id' => $ejaculation->id]) }}" class="text-muted"><small>{{ !empty($ejaculation->before_date) && !$ejaculation->discard_elapsed_time ? $ejaculation->before_date . ' ' : '' }}{{ $ejaculation->ejaculated_date->format('Y/m/d H:i') }}</small></a></h5>
</div>
<!-- tags -->
@if ($ejaculation->is_private || $ejaculation->source !== 'web' || $ejaculation->tags->isNotEmpty())

View File

@@ -20,29 +20,34 @@ class CienResolverTest extends TestCase
public function test()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Cien/test.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Cien/test.html');
$this->createResolver(CienResolver::class, $responseText);
$metadata = $this->resolver->resolve('https://ci-en.dlsite.com/creator/2462/article/87502');
$this->assertSame('進捗とボツ立ち絵', $metadata->title);
$this->assertSame('進捗とボツ立ち絵 - ねんない5 - Ci-en', $metadata->title);
$this->assertSame('ドット製D ACTを製作しています。' . PHP_EOL . '恐ろしい存在に襲われる絶望感や、被虐的な官能がテーマです。', $metadata->description);
$this->assertStringStartsWith('https://media.ci-en.jp/private/attachment/creator/00002462/a7afd3b02a6d1caa6afe6a3bf5550fb6a42aefba686f17a0a2f63c97fd6867ab/image-800.jpg?px-time=', $metadata->image);
$this->assertStringStartsWith('https://media.ci-en.jp/private/attachment/creator/00002462/a7afd3b02a6d1caa6afe6a3bf5550fb6a42aefba686f17a0a2f63c97fd6867ab/image-web.jpg?jwt=', $metadata->image);
if ($this->shouldUseMock()) {
$this->assertSame('https://media.ci-en.jp/private/attachment/creator/00002462/a7afd3b02a6d1caa6afe6a3bf5550fb6a42aefba686f17a0a2f63c97fd6867ab/image-800.jpg?px-time=1568231879&px-hash=70c57e9a73d5afb4ac5363d1f37a851af8e0cb1f', $metadata->image);
$this->assertSame(1568235479, $metadata->expires_at->timestamp);
$this->assertSame('https://media.ci-en.jp/private/attachment/creator/00002462/a7afd3b02a6d1caa6afe6a3bf5550fb6a42aefba686f17a0a2f63c97fd6867ab/image-web.jpg?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJrZXkiOiJqd3RhdXRoX3NlY18yMDIwT2N0IiwiaXNzIjoiaHR0cHM6XC9cL2NpLWVuLmRsc2l0ZS5jb21cLyIsInN1YiI6IjAwMDAwMDAwMDAwIiwiYXVkIjoiYTdhZmQzYjAyYTZkMWNhYTZhZmU2YTNiZjU1NTBmYjZhNDJhZWZiYTY4NmYxN2EwYTJmNjNjOTdmZDY4NjdhYiIsImV4cCI6MTYwNzA2NzMyOX0.-462_WtZ6AUOxrfndBE-0_oWHKwesP9mMMn6K2oYQJM', $metadata->image);
$this->assertSame(1607067329, $metadata->expires_at->timestamp);
$this->assertSame('https://ci-en.dlsite.com/creator/2462/article/87502', (string) $this->handler->getLastRequest()->getUri());
}
}
public function testWithNoTimestamp()
public function testWithNoPostImage()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Cien/testWithNoTimestamp.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Cien/testWithNoPostImage.html');
$this->createResolver(CienResolver::class, $responseText);
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessage('Parameter "px-time" not found. Image=https://ci-en.dlsite.com/assets/img/common/logo_Ci-en_R18.svg Source=https://ci-en.dlsite.com/');
$this->resolver->resolve('https://ci-en.dlsite.com/');
$metadata = $this->resolver->resolve('https://ci-en.dlsite.com/creator/148/article/401866');
$this->assertSame('近況報告 - 薄稀 - Ci-en', $metadata->title);
$this->assertSame('サキュバスをはじめ、M向けの魔物娘をよく描くエロ絵描きです(´ω`)', $metadata->description);
$this->assertSame('https://media.ci-en.jp/public/cover/creator/00000148/9153a13f78591bc2c9efae1021a26f9b90d24d3b30a0b3e699d0050f09dab6df/image-990-c.jpg', $metadata->image);
if ($this->shouldUseMock()) {
$this->assertNull($metadata->expires_at);
$this->assertSame('https://ci-en.dlsite.com/creator/148/article/401866', (string) $this->handler->getLastRequest()->getUri());
}
}
}

View File

@@ -8,6 +8,8 @@ use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Response;
use Monolog\Handler\AbstractHandler;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
trait CreateMockedResolver
{
@@ -21,6 +23,18 @@ trait CreateMockedResolver
*/
protected $handler;
/**
* @var string
*/
protected $snapshotFilename;
protected function fetchSnapshot(string $filename): string
{
$this->snapshotFilename = $filename;
return file_get_contents($filename);
}
/**
* @param string $resolverClass
* @param string $responseText
@@ -30,19 +44,27 @@ trait CreateMockedResolver
*/
protected function createResolver(string $resolverClass, string $responseText, array $headers = [], int $status = 200)
{
if (!$this->shouldUseMock()) {
if (!$this->shouldUseMock() && !$this->shouldUpdateSnapshot()) {
$this->resolver = app()->make($resolverClass);
return $this->resolver;
}
$headers += [
'content-type' => 'text/html',
];
if ($this->shouldUseMock()) {
$headers += [
'content-type' => 'text/html',
];
$mockResponse = new Response($status, $headers, $responseText);
$this->handler = new MockHandler([$mockResponse]);
}
$stack = HandlerStack::create($this->handler);
$client = new Client(['handler' => $stack]);
if ($this->shouldUpdateSnapshot()) {
$stack->push($this->makeUpdateSnapshotMiddleware());
}
$mockResponse = new Response($status, $headers, $responseText);
$this->handler = new MockHandler([$mockResponse]);
$client = new Client(['handler' => HandlerStack::create($this->handler)]);
$this->resolver = app()->make($resolverClass, ['client' => $client]);
return $this->resolver;
@@ -52,4 +74,27 @@ trait CreateMockedResolver
{
return (bool) env('TEST_USE_HTTP_MOCK', true);
}
protected function shouldUpdateSnapshot(): bool
{
return (bool) env('TEST_UPDATE_SNAPSHOT', false);
}
protected function makeUpdateSnapshotMiddleware(): callable
{
return function (callable $next) {
return function (RequestInterface $request, array $options) use ($next) {
return $next($request, $options)->then(function (ResponseInterface $response) {
if (empty($this->snapshotFilename)) {
throw new \RuntimeException('スナップショットのファイル名が分かりません。file_get_contents()を使っている場合、fetchSnapshot()に置き換えてください。');
}
file_put_contents($this->snapshotFilename, (string) $response->getBody());
fwrite(STDERR, "Snapshot Updated: {$this->snapshotFilename}\n");
return $response;
});
};
};
}
}

View File

@@ -20,7 +20,7 @@ class DLsiteResolverTest extends TestCase
public function testHome()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/DLsite/testHome.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/DLsite/testHome.html');
$this->createResolver(DLsiteResolver::class, $responseText);
@@ -36,7 +36,7 @@ class DLsiteResolverTest extends TestCase
public function testSoft()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/DLsite/testSoft.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/DLsite/testSoft.html');
$this->createResolver(DLsiteResolver::class, $responseText);
@@ -52,7 +52,7 @@ class DLsiteResolverTest extends TestCase
public function testComic()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/DLsite/testComic.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/DLsite/testComic.html');
$this->createResolver(DLsiteResolver::class, $responseText);
@@ -68,7 +68,7 @@ class DLsiteResolverTest extends TestCase
public function testManiax()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/DLsite/testManiax.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/DLsite/testManiax.html');
$this->createResolver(DLsiteResolver::class, $responseText);
@@ -84,7 +84,7 @@ class DLsiteResolverTest extends TestCase
public function testPro()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/DLsite/testPro.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/DLsite/testPro.html');
$this->createResolver(DLsiteResolver::class, $responseText);
@@ -100,7 +100,7 @@ class DLsiteResolverTest extends TestCase
public function testBooks()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/DLsite/testBooks.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/DLsite/testBooks.html');
$this->createResolver(DLsiteResolver::class, $responseText);
@@ -116,7 +116,7 @@ class DLsiteResolverTest extends TestCase
public function testGirls()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/DLsite/testGirls.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/DLsite/testGirls.html');
$this->createResolver(DLsiteResolver::class, $responseText);
@@ -132,7 +132,7 @@ class DLsiteResolverTest extends TestCase
public function testGirlsPro()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/DLsite/testGirlsPro.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/DLsite/testGirlsPro.html');
$this->createResolver(DLsiteResolver::class, $responseText);
@@ -148,7 +148,7 @@ class DLsiteResolverTest extends TestCase
public function testBL()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/DLsite/testBL.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/DLsite/testBL.html');
$this->createResolver(DLsiteResolver::class, $responseText);
@@ -164,7 +164,7 @@ class DLsiteResolverTest extends TestCase
public function testEng()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/DLsite/testEng.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/DLsite/testEng.html');
$this->createResolver(DLsiteResolver::class, $responseText);
@@ -180,7 +180,7 @@ class DLsiteResolverTest extends TestCase
public function testEcchiEng()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/DLsite/testEcchiEng.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/DLsite/testEcchiEng.html');
$this->createResolver(DLsiteResolver::class, $responseText);
@@ -196,7 +196,7 @@ class DLsiteResolverTest extends TestCase
public function testSPLink()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/DLsite/testHome.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/DLsite/testHome.html');
// SP版touchのURLのテストだがリゾルバ側でURLから-touchを削除してPC版を取得するので、PC版の内容を使用する
$this->createResolver(DLsiteResolver::class, $responseText);
@@ -213,7 +213,7 @@ class DLsiteResolverTest extends TestCase
public function testShortLink()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/DLsite/testHome.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/DLsite/testHome.html');
$this->createResolver(DLsiteResolver::class, $responseText);
@@ -229,7 +229,7 @@ class DLsiteResolverTest extends TestCase
public function testOldAffiliateLink()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/DLsite/testHome.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/DLsite/testHome.html');
$this->createResolver(DLsiteResolver::class, $responseText);
@@ -245,7 +245,7 @@ class DLsiteResolverTest extends TestCase
public function testSnsAffiliateLink()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/DLsite/testHome.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/DLsite/testHome.html');
$this->createResolver(DLsiteResolver::class, $responseText);
@@ -261,7 +261,7 @@ class DLsiteResolverTest extends TestCase
public function testAffiliateLink()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/DLsite/testHome.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/DLsite/testHome.html');
$this->createResolver(DLsiteResolver::class, $responseText);
@@ -277,7 +277,7 @@ class DLsiteResolverTest extends TestCase
public function testAffiliateUrl()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/DLsite/testHome.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/DLsite/testHome.html');
$this->createResolver(DLsiteResolver::class, $responseText);
@@ -303,7 +303,7 @@ class DLsiteResolverTest extends TestCase
public function testHTMLdescription()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/DLsite/testHTMLdescription.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/DLsite/testHTMLdescription.html');
$this->createResolver(DLsiteResolver::class, $responseText);

View File

@@ -20,7 +20,7 @@ class DeviantArtResolverTest extends TestCase
public function testMature()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/DeviantArt/mature.json');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/DeviantArt/mature.json');
$this->createResolver(DeviantArtResolver::class, $responseText);

View File

@@ -20,7 +20,7 @@ class FC2ContentsResolverTest extends TestCase
public function testAdult()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/FC2Contents/adult.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/FC2Contents/adult.html');
$this->createResolver(FC2ContentsResolver::class, $responseText);
@@ -35,7 +35,7 @@ class FC2ContentsResolverTest extends TestCase
public function testGeneral()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/FC2Contents/general.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/FC2Contents/general.html');
$this->createResolver(FC2ContentsResolver::class, $responseText);

View File

@@ -20,7 +20,7 @@ class FantiaResolverTest extends TestCase
public function test()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Fantia/test.json');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Fantia/test.json');
$this->createResolver(FantiaResolver::class, $responseText);

View File

@@ -23,7 +23,7 @@ class FanzaResolverTest extends TestCase
*/
public function test($filename, $url, $title, $description, $image, $tags)
{
$responseText = file_get_contents(__DIR__ . "/../../fixture/Fanza/{$filename}");
$responseText = $this->fetchSnapshot(__DIR__ . "/../../fixture/Fanza/{$filename}");
$this->createResolver(FanzaResolver::class, $responseText);

View File

@@ -20,7 +20,7 @@ class HentaiFoundryResolverTest extends TestCase
public function test()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/HentaiFoundry/illust.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/HentaiFoundry/illust.html');
$this->createResolver(HentaiFoundryResolver::class, $responseText);

View File

@@ -20,7 +20,7 @@ class IwaraResolverTest extends TestCase
public function testVideo()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Iwara/video.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Iwara/video.html');
$this->createResolver(IwaraResolver::class, $responseText);
@@ -37,7 +37,7 @@ class IwaraResolverTest extends TestCase
public function testYouTube()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Iwara/youtube.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Iwara/youtube.html');
$this->createResolver(IwaraResolver::class, $responseText);
@@ -54,7 +54,7 @@ class IwaraResolverTest extends TestCase
public function testImages()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Iwara/images.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Iwara/images.html');
$this->createResolver(IwaraResolver::class, $responseText);

View File

@@ -20,7 +20,7 @@ class Kb10uyShortStoryServerResolverTest extends TestCase
public function testNormalPost()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Kb10uyShortStoryServer/tomone.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Kb10uyShortStoryServer/tomone.html');
$this->createResolver(Kb10uyShortStoryServerResolver::class, $responseText);

View File

@@ -20,7 +20,7 @@ class KomifloResolverTest extends TestCase
public function testComic()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Komiflo/comic.json');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Komiflo/comic.json');
$this->createResolver(KomifloResolver::class, $responseText);
@@ -36,7 +36,7 @@ class KomifloResolverTest extends TestCase
public function testComicWithNoParents()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Komiflo/comicWithNoParents.json');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Komiflo/comicWithNoParents.json');
$this->createResolver(KomifloResolver::class, $responseText);

View File

@@ -21,7 +21,7 @@ class NicoSeigaResolverTest extends TestCase
public function testSeiga()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/NicoSeiga/seiga.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/NicoSeiga/seiga.html');
$this->createResolver(NicoSeigaResolver::class, $responseText);
@@ -37,7 +37,7 @@ class NicoSeigaResolverTest extends TestCase
public function testShunga()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/NicoSeiga/shunga.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/NicoSeiga/shunga.html');
$this->createResolver(NicoSeigaResolver::class, $responseText);

View File

@@ -20,7 +20,7 @@ class NijieResolverTest extends TestCase
public function testStandardPicture()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Nijie/testStandardPictureResponse.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Nijie/testStandardPictureResponse.html');
$this->createResolver(NijieResolver::class, $responseText);
@@ -36,7 +36,7 @@ class NijieResolverTest extends TestCase
public function testMultiplePicture()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Nijie/testMultiplePictureResponse.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Nijie/testMultiplePictureResponse.html');
$this->createResolver(NijieResolver::class, $responseText);
@@ -52,7 +52,7 @@ class NijieResolverTest extends TestCase
public function testAnimationGif()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Nijie/testAnimationGifResponse.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Nijie/testAnimationGifResponse.html');
$this->createResolver(NijieResolver::class, $responseText);
@@ -68,7 +68,7 @@ class NijieResolverTest extends TestCase
public function testMp4Movie()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Nijie/testMp4MovieResponse.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Nijie/testMp4MovieResponse.html');
$this->createResolver(NijieResolver::class, $responseText);
@@ -84,7 +84,7 @@ class NijieResolverTest extends TestCase
public function testViewPopup()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Nijie/testStandardPictureResponse.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Nijie/testStandardPictureResponse.html');
$this->createResolver(NijieResolver::class, $responseText);
@@ -100,7 +100,7 @@ class NijieResolverTest extends TestCase
public function testSp()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Nijie/testStandardPictureResponse.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Nijie/testStandardPictureResponse.html');
$this->createResolver(NijieResolver::class, $responseText);
@@ -116,7 +116,7 @@ class NijieResolverTest extends TestCase
public function testSpViewPopup()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Nijie/testStandardPictureResponse.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Nijie/testStandardPictureResponse.html');
$this->createResolver(NijieResolver::class, $responseText);
@@ -132,7 +132,7 @@ class NijieResolverTest extends TestCase
public function testHasHtmlInAuthorProfile()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Nijie/testHasHtmlInAuthorProfileResponse.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Nijie/testHasHtmlInAuthorProfileResponse.html');
$this->createResolver(NijieResolver::class, $responseText);

View File

@@ -20,7 +20,7 @@ class PixivResolverTest extends TestCase
public function testIllust()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Pixiv/illust.json');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Pixiv/illust.json');
$this->createResolver(PixivResolver::class, $responseText);
@@ -36,7 +36,7 @@ class PixivResolverTest extends TestCase
public function testIllustMultiPages()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Pixiv/illustMultiPages.json');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Pixiv/illustMultiPages.json');
$this->createResolver(PixivResolver::class, $responseText);
@@ -52,7 +52,7 @@ class PixivResolverTest extends TestCase
public function testManga()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Pixiv/manga.json');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Pixiv/manga.json');
$this->createResolver(PixivResolver::class, $responseText);
@@ -68,7 +68,7 @@ class PixivResolverTest extends TestCase
public function testArtworkUrl()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Pixiv/illust.json');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Pixiv/illust.json');
$this->createResolver(PixivResolver::class, $responseText);
@@ -84,7 +84,7 @@ class PixivResolverTest extends TestCase
public function testArtworkUrlEn()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Pixiv/illust.json');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Pixiv/illust.json');
$this->createResolver(PixivResolver::class, $responseText);

View File

@@ -20,7 +20,7 @@ class PlurkResolverTest extends TestCase
public function test()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Plurk/test.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Plurk/test.html');
$this->createResolver(PlurkResolver::class, $responseText);

View File

@@ -20,7 +20,7 @@ class SteamResolverTest extends TestCase
public function test()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Steam/test.json');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Steam/test.json');
$this->createResolver(SteamResolver::class, $responseText);
@@ -32,7 +32,7 @@ class SteamResolverTest extends TestCase
public function testR18()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Steam/testR18.json');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Steam/testR18.json');
$this->createResolver(SteamResolver::class, $responseText);
@@ -46,7 +46,7 @@ class SteamResolverTest extends TestCase
{
$this->expectException(\RuntimeException::class);
$responseText = file_get_contents(__DIR__ . '/../../fixture/Steam/testNotFound.json');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Steam/testNotFound.json');
$this->createResolver(SteamResolver::class, $responseText);

View File

@@ -20,7 +20,7 @@ class ToranoanaResolverTest extends TestCase
public function testTora()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Toranoana/testTora.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Toranoana/testTora.html');
$this->createResolver(ToranoanaResolver::class, $responseText);
@@ -35,7 +35,7 @@ class ToranoanaResolverTest extends TestCase
public function testToraR()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Toranoana/testToraR.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Toranoana/testToraR.html');
$this->createResolver(ToranoanaResolver::class, $responseText);
@@ -50,7 +50,7 @@ class ToranoanaResolverTest extends TestCase
public function testToraD()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Toranoana/testToraD.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Toranoana/testToraD.html');
$this->createResolver(ToranoanaResolver::class, $responseText);
@@ -65,7 +65,7 @@ class ToranoanaResolverTest extends TestCase
public function testToraRD()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Toranoana/testToraRD.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Toranoana/testToraRD.html');
$this->createResolver(ToranoanaResolver::class, $responseText);
@@ -80,7 +80,7 @@ class ToranoanaResolverTest extends TestCase
public function testJoshi()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Toranoana/testJoshi.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Toranoana/testJoshi.html');
$this->createResolver(ToranoanaResolver::class, $responseText);
@@ -95,7 +95,7 @@ class ToranoanaResolverTest extends TestCase
public function testJoshiR()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Toranoana/testJoshiR.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Toranoana/testJoshiR.html');
$this->createResolver(ToranoanaResolver::class, $responseText);
@@ -110,7 +110,7 @@ class ToranoanaResolverTest extends TestCase
public function testJoshiD()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Toranoana/testJoshiD.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Toranoana/testJoshiD.html');
$this->createResolver(ToranoanaResolver::class, $responseText);
@@ -125,7 +125,7 @@ class ToranoanaResolverTest extends TestCase
public function testJoshiRD()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Toranoana/testJoshiRD.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Toranoana/testJoshiRD.html');
$this->createResolver(ToranoanaResolver::class, $responseText);

View File

@@ -20,7 +20,7 @@ class XtubeResolverTest extends TestCase
public function test()
{
$responseText = file_get_contents(__DIR__ . '/../../fixture/Xtube/video.html');
$responseText = $this->fetchSnapshot(__DIR__ . '/../../fixture/Xtube/video.html');
$this->createResolver(XtubeResolver::class, $responseText);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,602 +0,0 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta name="google-site-verification" content="4UtUmaro4aJIR94PZdv-GoliXlDvtUVFL03-9CTh68s" />
<meta charset="UTF-8">
<meta name="viewport" id="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, viewport-fit=cover">
<meta name="csrf-token" content="1UAeVYZqG3XqR5XwRi0MXYJn3zIf51glrKZKY2gp">
<meta name="app-auth-check" content="0">
<meta property="og:title" content="Ci-en">
<meta property="og:type" content="website">
<meta property="og:url" content="http://ci-en.dlsite.com">
<meta property="og:image" content="https://ci-en.dlsite.com/assets/img/common/logo_Ci-en_R18.svg">
<meta property="og:site_name" content="Ci-en">
<meta property="og:description" content="好きの気持ちは、カタチで伝えよう。">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="Ci-en">
<meta name="twitter:description" content="好きの気持ちは、カタチで伝えよう。">
<meta name="twitter:image:src" content="https://ci-en.dlsite.com/assets/img/common/logo_Ci-en_R18.svg">
<meta name="description" content="好きの気持ちは、カタチで伝えよう。">
<meta name="keyword" content="Ci-en">
<meta name="sentry-public-dsn" content="7319f62f11fe408b932254c5fe87eb64@sentry.io/301968">
<meta name="sentry-release" content="fd2635a6350eda85e4dbec5559f0172e7f8086df">
<meta name="app-locale" content="ja">
<title>好きの気持ちは、カタチで伝えよう。 - Ci-en</title>
<link media="all" type="text/css" rel="stylesheet" href="https://ci-en.dlsite.com/assets/css/app.css?1567667013">
<link rel="icon" href="/favicon.ico">
<link rel="stylesheet" href="https://www.dlsite.com/assets/share/css/universal/universal.css">
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-NNPHW5Z');</script>
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-109913020-1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){
dataLayer.push(arguments);
}
gtag('js', new Date());
gtag('config', 'UA-109913020-1', {
'send_page_view': false,
'custom_map': {
'dimension1':'logined',
},
});
gtag('set', 'linker', {
'accept_incoming': true,
'domains': ['ci-en.net','ci-en.dlsite.com']
});
gtag('event', 'page_view', {
'logined': '',
'has_creator': '0',
});
</script>
</head>
<body class="global-layout p-topPage ">
<!-- グローバルヘッダー -->
<div class="l-eisysGroupHeader type-cien">
<vue-global-header
account-settings-url="https://login.dlsite.com/user/self?redirect_uri=https%3A%2F%2Fci-en.dlsite.com%2Flogout&amp;lang=ja"
is-adult="1"
user-id=""
creator-id=""
></vue-global-header>
</div>
<header class="global-layout-item type-header">
<div class="header-inner">
<div class="cien-logo type-r18">
<a href="/">Ci-en</a>
</div>
<form method="GET" action="https://ci-en.dlsite.com/search" accept-charset="UTF-8" class="hd-searchBox">
<input type="text" class="hd-searchInput" name="keyword" placeholder="クリエイターを検索">
<input type="submit" class="hd-searchButton" value="&#xe90f;">
</form>
<div class="nav-drawer type-menu">
<input id="nav-inputMenu" type="checkbox" class="nav-unshown">
<label id="nav-open" for="nav-inputMenu">
<span></span>
</label>
<label class="nav-unshown icon-navClose" id="nav-close" for="nav-inputMenu"><span></span></label>
<div class="nav-content type-left">
<div class="navEntry">
<p class="text">DLsiteアカウントをお持ちの方はログインできます。</p>
<div class="btnBox">
<a href="https://ci-en.dlsite.com/login" class="btn type-basic">ログイン</a>
<a href="https://ci-en.dlsite.com/login" class="btn type-important">新規登録</a>
</div>
<p class="notice">株式会社エイシスが運営しているサービスをDLsiteアカウント一つでご利用いただけます。</p>
</div>
<ul class="nav-submenuList">
<li class="nav-submenuList-item"><a href="https://ci-en.dlsite.com/about/supporter">Ci-enとは</a></li>
<li class="nav-submenuList-item"><a href="https://ci-en.dlsite.com/about/creator">クリエイター登録</a></li>
<li class="nav-submenuList-item"><a href="https://ci-en.dlsite.com/about/faq">よくある質問</a></li>
</ul>
</div>
</div>
<div class="globalNav-wrap">
<ul class="globalNav is-guest">
<li class="globalNav-item type-bell">
<a href="https://ci-en.dlsite.com/mypage/activity">
<span class="globalNav-icon">通知</span>
</a>
</li>
<li class="globalNav-item type-searchBox">
<form method="GET" action="https://ci-en.dlsite.com/search" accept-charset="UTF-8" class="hd-search">
<input type="submit" class="hd-searchButton" value="&#xe90f;">
<input type="text" class="hd-searchInput" name="keyword" placeholder="クリエイターを検索">
</form>
</li>
<li class="globalNav-item type-search ">
<a href="https://ci-en.dlsite.com/search/top">
<span class="globalNav-icon"></span>
</a>
</li>
<li class="globalNav-item type-signup">
<a href="https://ci-en.dlsite.com/login">Ci-enをはじめる</a>
</li>
<li class="globalNav-item type-mypage">
<a href="https://ci-en.dlsite.com/mypage">
<span class="globalNav-icon">マイページ</span>
</a>
</li>
<li class="globalNav-item type-help">
<a href="https://ci-en.dlsite.com/about/faq">
<span class="globalNav-icon">ヘルプ</span>
</a>
</li>
</ul>
</div>
</div>
</header>
<section class="global-layout-item type-contentsNav">
</section>
<section id="detail" class="global-layout-item type-contents">
<section class="grid-container inner-layout">
<div class="topHeroArea" onload="console.log('loaded');">
<h1 class="topCatchcopy">
<div class="catchcopy-item type-first"></div>
<div class="catchcopy-item type-last"></div>
</h1>
<div id="top-heroarea-mainimg" class="topHeroArea-mainImg">
<div class="mainImg-item item-twinkleStar"></div>
<div class="mainImg-item item-twinkleStar1"></div>
<div class="mainImg-item item-twinkleStar2"></div>
<div class="mainImg-item item-star"></div>
<div class="topHeroArea-gradeFilter"></div>
<div class="mainImg-item item-wood"></div>
<div class="mainImg-item item-donguri"></div>
<div class="mainImg-item item-present"></div>
<div class="mainImg-item item-letter"></div>
</div>
</div>
<div class="grid-item grid-main">
<div class="topIntroArea">
<h1 class="topIntroArea-heading"></h1>
<div class="topIntroArea-textGroup">
<p class="text">新しいものを作るのは、簡単なことではありません。<span>思いを形にするには時間と手間、そして資金が必要です。</span></p>
<p class="text">Ci-enで好きなクリエイターを支援すれば、<span>その収益を創作活動に活かすことができるようになります。</span></p>
<p class="text">クリエイターも支援者も、誰もが創作を楽しめる世界に参加してみませんか?</p>
</div>
<div class="topIntroArea-btn">
<a href="https://login.dlsite.com/register?redirect_uri=https%3A%2F%2Fci-en.dlsite.com&amp;lang=ja" class="btn type-important-confirm">Ci-enをはじめる</a>
</div>
</div>
<div class="topAboutCienArea">
<div class="topAboutCienArea-main"></div>
<div class="topAboutCienArea-btn">
<a href="https://ci-en.dlsite.com/about/supporter" class="btn type-confirm">もっと知りたい方はこちら</a>
</div>
</div>
<div id="follow" class="topLetsFollowArea">
<div class="topLetsFollowArea-heading"></div>
<div class="topLetsFollowArea-body">
<div class="topShowcase type-popularCreator">
<div class="topShowcase-heading">
<a href="//ci-en.net#follow" class="btn type-topRatingChange">全年齢に切替</a>
</div>
<div class="topShowcase-body">
<div class="mod-creatorCard at-topPage">
<div class="creatorCard-header">
<img src="https://media.ci-en.jp/public/cover/creator/00000208/6742fc1b379a180e4485cdeff9a086d535725683ca882155b400bc65ab13ed3e/image-990-c.jpg" alt="">
</div>
<div class="creatorCard-body">
<dt class="creatorCard-thumb">
<div class="accountIcon type-cerator size-m">
<img src="https://media.ci-en.jp/public/icon/creator/00000208/6f0dac0278bbb547e97b0deddd2aad22043d6b9ce8cde868b99e543d7dc1ec9f/image-200-c.jpg" alt="">
</div>
</dt>
<dd class="creatorCard-name">ONEONE1</dd>
<dd class="creatorCard-tag">
<ul class="tagList type-creator">
<li class="tagList-item type-creator">
<a class="item-tag type-activityGenre" href="/search?categoryId=9">ゲーム</a>
</li>
</ul>
</dd>
</div>
<a href="https://ci-en.dlsite.com/creator/208" class="creatorCard-link"></a>
</div>
<div class="mod-creatorCard at-topPage">
<div class="creatorCard-header">
<img src="https://media.ci-en.jp/public/cover/creator/00001145/4f39516e4f22c76b45443b5567789419d8d0ea985958cd26adea88cf79c95fd3/image-990-c.jpg" alt="">
</div>
<div class="creatorCard-body">
<dt class="creatorCard-thumb">
<div class="accountIcon type-cerator size-m">
<img src="https://media.ci-en.jp/public/icon/creator/00001145/5c18c657f97e23ea73a700784f55c2e34b1871e532b45e32118ee57a6c2cb677/image-200-c.jpg" alt="">
</div>
</dt>
<dd class="creatorCard-name">同人サークルGyu!</dd>
<dd class="creatorCard-tag">
<ul class="tagList type-creator">
<li class="tagList-item type-creator">
<a class="item-tag type-activityGenre" href="/search?categoryId=9">ゲーム</a>
</li>
</ul>
</dd>
</div>
<a href="https://ci-en.dlsite.com/creator/1145" class="creatorCard-link"></a>
</div>
<div class="mod-creatorCard at-topPage">
<div class="creatorCard-header">
<img src="https://media.ci-en.jp/public/cover/creator/00000057/a8c0cf4f84fc374e9ba5891ee2a158a69067eee0d3601a66e1e00489df4df25d/image-990-c.jpg" alt="">
</div>
<div class="creatorCard-body">
<dt class="creatorCard-thumb">
<div class="accountIcon type-cerator size-m">
<img src="https://media.ci-en.jp/public/icon/creator/00000057/72fbd8b3e2124f88de11866d21a23c6e4ff375e62dba50c517f537655ff2e981/image-200-c.jpg" alt="">
</div>
</dt>
<dd class="creatorCard-name">クリメニア</dd>
<dd class="creatorCard-tag">
<ul class="tagList type-creator">
<li class="tagList-item type-creator">
<a class="item-tag type-activityGenre" href="/search?categoryId=9">ゲーム</a>
</li>
</ul>
</dd>
</div>
<a href="https://ci-en.dlsite.com/creator/57" class="creatorCard-link"></a>
</div>
<div class="mod-creatorCard at-topPage">
<div class="creatorCard-header">
<img src="https://media.ci-en.jp/public/cover/creator/00001321/5e327eb84b4ce6a36be637729b000ba74a9b27f35273f08a30b86e464ee1e25e/image-990-c.jpg" alt="">
</div>
<div class="creatorCard-body">
<dt class="creatorCard-thumb">
<div class="accountIcon type-cerator size-m">
<img src="https://media.ci-en.jp/public/icon/creator/00001321/60557adcbb149f7494c6aed36f3d44373896a971b997af3e4e02a650f70f5cbe/image-200-c.jpg" alt="">
</div>
</dt>
<dd class="creatorCard-name">Hypnotic Yanh</dd>
<dd class="creatorCard-tag">
<ul class="tagList type-creator">
<li class="tagList-item type-creator">
<a class="item-tag type-activityGenre" href="/search?categoryId=8">音声作品</a>
</li>
</ul>
</dd>
</div>
<a href="https://ci-en.dlsite.com/creator/1321" class="creatorCard-link"></a>
</div>
<div class="mod-creatorCard at-topPage">
<div class="creatorCard-header">
<img src="https://media.ci-en.jp/public/cover/creator/00000391/f0134aaa1e2174efabc30e024c973024f34064e0f6ab6738477564c34170ae3b/image-990-c.jpg" alt="">
</div>
<div class="creatorCard-body">
<dt class="creatorCard-thumb">
<div class="accountIcon type-cerator size-m">
<img src="https://media.ci-en.jp/public/icon/creator/00000391/c98d97cf4d4af1c6aad452150693a06a63e6f4323e21ad0848e83f910a949b80/image-200-c.jpg" alt="">
</div>
</dt>
<dd class="creatorCard-name">シロクマの嫁(伊ヶ崎綾香)</dd>
<dd class="creatorCard-tag">
<ul class="tagList type-creator">
<li class="tagList-item type-creator">
<a class="item-tag type-activityGenre" href="/search?categoryId=8">音声作品</a>
</li>
</ul>
</dd>
</div>
<a href="https://ci-en.dlsite.com/creator/391" class="creatorCard-link"></a>
</div>
<div class="mod-creatorCard at-topPage">
<div class="creatorCard-header">
<img src="https://media.ci-en.jp/public/cover/creator/00001058/14ccafc478078692f53a62c0e2ea722d55dd018945d44c31e55bdcc237ee9944/image-990-c.jpg" alt="">
</div>
<div class="creatorCard-body">
<dt class="creatorCard-thumb">
<div class="accountIcon type-cerator size-m">
<img src="https://media.ci-en.jp/public/icon/creator/00001058/3ac1827236a6fce3d5d7d9142dd4e77e3b732b63db18af81e5c74818572d7b10/image-200-c.jpg" alt="">
</div>
</dt>
<dd class="creatorCard-name">鉱油/73号坑道</dd>
<dd class="creatorCard-tag">
<ul class="tagList type-creator">
<li class="tagList-item type-creator">
<a class="item-tag type-activityGenre" href="/search?categoryId=9">ゲーム</a>
</li>
</ul>
</dd>
</div>
<a href="https://ci-en.dlsite.com/creator/1058" class="creatorCard-link"></a>
</div>
<div class="mod-creatorCard at-topPage">
<div class="creatorCard-header">
<img src="https://media.ci-en.jp/public/cover/creator/00000190/0ebc04b8de8d6e42f6c5bf020936bff79bdfa29ab71f1ea2eff547f42fd9caa7/image-990-c.jpg" alt="">
</div>
<div class="creatorCard-body">
<dt class="creatorCard-thumb">
<div class="accountIcon type-cerator size-m">
<img src="https://media.ci-en.jp/public/icon/creator/00000190/e5e01272dac25575a00b651adf8d04524d91a87ff534bc4391dbabea404e6a49/image-200-c.jpg" alt="">
</div>
</dt>
<dd class="creatorCard-name">ぽいずん</dd>
<dd class="creatorCard-tag">
<ul class="tagList type-creator">
<li class="tagList-item type-creator">
<a class="item-tag type-activityGenre" href="/search?categoryId=9">ゲーム</a>
</li>
</ul>
</dd>
</div>
<a href="https://ci-en.dlsite.com/creator/190" class="creatorCard-link"></a>
</div>
<div class="mod-creatorCard at-topPage">
<div class="creatorCard-header">
<img src="https://media.ci-en.jp/public/cover/creator/00002004/b568c5bcc1108db1276c32b550140f5d539a92c589f3bd8fc16163fba56eb50a/image-990-c.jpg" alt="">
</div>
<div class="creatorCard-body">
<dt class="creatorCard-thumb">
<div class="accountIcon type-cerator size-m">
<img src="https://media.ci-en.jp/public/icon/creator/00002004/aab5ff3de14c0361715b4a16cc3cc6961cac72b84762d514e6fc38c40dda81e6/image-200-c.jpg" alt="">
</div>
</dt>
<dd class="creatorCard-name">あいすシチュー</dd>
<dd class="creatorCard-tag">
<ul class="tagList type-creator">
<li class="tagList-item type-creator">
<a class="item-tag type-activityGenre" href="/search?categoryId=9">ゲーム</a>
</li>
</ul>
</dd>
</div>
<a href="https://ci-en.dlsite.com/creator/2004" class="creatorCard-link"></a>
</div>
<div class="mod-creatorCard at-topPage">
<div class="creatorCard-header">
<img src="https://media.ci-en.jp/public/cover/creator/00001191/b0ca242c5095531d78a95f3bc4e15a5b642b33c808f91af1cf95723dee7a4543/image-990-c.jpg" alt="">
</div>
<div class="creatorCard-body">
<dt class="creatorCard-thumb">
<div class="accountIcon type-cerator size-m">
<img src="https://media.ci-en.jp/public/icon/creator/00001191/950a61d5e3648e8c01c31a9a3ee127c8cb8e5e015379feb45d409f61acf9cf5a/image-200-c.jpg" alt="">
</div>
</dt>
<dd class="creatorCard-name">みこにそみ</dd>
<dd class="creatorCard-tag">
<ul class="tagList type-creator">
<li class="tagList-item type-creator">
<a class="item-tag type-activityGenre" href="/search?categoryId=9">ゲーム</a>
</li>
</ul>
</dd>
</div>
<a href="https://ci-en.dlsite.com/creator/1191" class="creatorCard-link"></a>
</div>
<div class="mod-creatorCard at-topPage">
<div class="creatorCard-header">
<img src="https://media.ci-en.jp/public/cover/creator/00000944/8b3b9c5cc1024bf0b532b9a0db168db7600a77ed6fbeaf15eb4c56656659666d/image-990-c.jpg" alt="">
</div>
<div class="creatorCard-body">
<dt class="creatorCard-thumb">
<div class="accountIcon type-cerator size-m">
<img src="https://media.ci-en.jp/public/icon/creator/00000944/9dc7439b9801ac6f0e66e92f8980fb2771cb8ab2f8a05e230c753d0f953ce4e0/image-200-c.jpg" alt="">
</div>
</dt>
<dd class="creatorCard-name">D-LIS-ディーリス</dd>
<dd class="creatorCard-tag">
<ul class="tagList type-creator">
<li class="tagList-item type-creator">
<a class="item-tag type-activityGenre" href="/search?categoryId=9">ゲーム</a>
</li>
</ul>
</dd>
</div>
<a href="https://ci-en.dlsite.com/creator/944" class="creatorCard-link"></a>
</div>
</div>
</div>
<div class="topShowcase type-searchByGenre">
<div class="topShowcase-heading"></div>
<div class="topShowcase-body">
<div class="topGenre-item">
<a href="/search?categoryId=9" class="topGenre-link">
ゲーム
</a>
</div>
<div class="topGenre-item">
<a href="/search?categoryId=1" class="topGenre-link">
イラスト
</a>
</div>
<div class="topGenre-item">
<a href="/search?categoryId=2" class="topGenre-link">
漫画
</a>
</div>
<div class="topGenre-item">
<a href="/search?categoryId=8" class="topGenre-link">
音声作品
</a>
</div>
<div class="topGenre-item">
<a href="/search?categoryId=3" class="topGenre-link">
小説
</a>
</div>
<div class="topGenre-item">
<a href="/search?categoryId=7" class="topGenre-link">
声優・歌い手
</a>
</div>
<div class="topGenre-item">
<a href="/search?categoryId=12" class="topGenre-link">
映像・アニメ
</a>
</div>
<div class="topGenre-item">
<a href="/search?categoryId=17" class="topGenre-link">
その他
</a>
</div>
<div class="topGenre-item">
<a href="/search?categoryId=10" class="topGenre-link">
YouTuber・実況
</a>
</div>
<div class="topGenre-item">
<a href="/search?categoryId=14" class="topGenre-link">
VR
</a>
</div>
</div>
</div>
</div>
</div>
<div class="topRegisterCreatorArea">
<div class="topRegisterCreatorArea-body">
<div class="topRegisterCreatorArea-main">
<div class="topRegisterCreatorArea-mainImg"></div>
<div class="topRegisterCreatorArea-btn">
<a href="https://ci-en.dlsite.com/about/creator" class="btn type-important-confirm">クリエイター登録について</a>
</div>
</div>
</div>
</div>
<div class="topRegisterArea">
<div class="topRegisterArea-body">
<div class="topRegisterArea-main">
<p class="topRegisterArea-text">DLsiteアカウントをお持ちの方はログインできます。</p>
<div class="btnBox">
<a href="https://ci-en.dlsite.com/login" class="btn type-confirm">ログイン</a>
<a href="https://login.dlsite.com/register?redirect_uri=https%3A%2F%2Fci-en.dlsite.com&amp;lang=ja" class="btn type-important-confirm" target="_blank">新規登録</a>
</div>
<p class="topRegisterArea-annotation">株式会社エイシスが運営しているサービスをDLsiteアカウント一つでご利用いただけます。</p>
</div>
</div>
</div>
</div>
</section>
</section>
<footer class="global-layout-item type-footer">
<div class="gotoTOPContainer">
<a href="#" class="ankerlink">ページトップ</a>
</div>
<div class="globalFooter">
<div class="footerContainer innerSpaceFooter">
<dl class="footerNav itemNum1">
<dt class="footerNav-title">Ci-enについて</dt>
<dd class="footerNav-item"><a href="https://ci-en.dlsite.com/about/supporter" class="footerLink">Ci-enとは</a></dd>
<dd class="footerNav-item"><a href="https://ci-en.dlsite.com/about/creator" class="footerLink">クリエイター登録</a></dd>
<dd class="footerNav-item"><a href="https://ci-en.dlsite.com/about/faq" class="footerLink">よくある質問(支援者)</a></dd>
<dd class="footerNav-item"><a href="https://ci-en.dlsite.com/about/creator-faq" class="footerLink">よくある質問(クリエイター)</a></dd>
<dd class="footerNav-item"><a href="https://ci-en.dlsite.com/inquiry" class="footerLink">お問い合わせ</a></dd>
<dd class="footerNav-item"><a href="http://info.ci-en.net" target="_blank" class="footerLink">お知らせブログ</a></dd>
</dl>
<dl class="footerNav itemNum2">
<dt class="footerNav-title">運営情報</dt>
<dd class="footerNav-item"><a href="http://www.eisys.co.jp/company/company-info.html" target="_blank" class="footerLink">会社概要</a></dd>
<dd class="footerNav-item"><a href="https://ci-en.dlsite.com/legal/regulation" class="footerLink">利用規約</a></dd>
<dd class="footerNav-item"><a href="https://ci-en.dlsite.com/legal/law" class="footerLink">特定商取引法に基づく表示</a></dd>
<dd class="footerNav-item"><a href="https://ci-en.dlsite.com/legal/censorship" class="footerLink">コンプライアンスポリシー</a></dd>
<dd class="footerNav-item"><a href="https://ci-en.dlsite.com/legal/privacy" class="footerLink">個人情報の取り扱いについて</a></dd>
</dl>
<div class="l-eisysGroupFooter type-cien is-sponly">
<div class="eisysGroupFooterInner">
<p class="eisysGroupFooterHeading">関連サービス</p>
<ul class="eisysGroupFooterService">
<li class="eisysGroupFooterService-link type-dlsite">
<a href="https://www.dlsite.com/maniax-touch/?utm_campaign=cien&amp;utm_medium=text&amp;utm_content=sp_globalfooter"><span>ダウンロードショップ</span>DLsite</a>
</li>
<li class="eisysGroupFooterService-link type-nijiyome">
<a href="https://www.nijiyome.jp/?en=cien&amp;em=text&amp;et=sp_globalfooter"><span>オンラインゲームサイト</span>にじよめ</a>
</li>
<li class="eisysGroupFooterService-link type-channel">
<a href="https://ch.dlsite.com/?from=sp_globalfooter_cien"><span>二次元コミュニティサイト</span>DLチャンネル</a>
</li>
<li class="eisysGroupFooterService-link type-chobit">
<a href="https://chobit.cc/?from=sp_globalfooter_cien"><span>無料体験版サイト</span>chobit</a>
</li>
<li class="eisysGroupFooterService-link type-triokini">
<a href="https://triokini.com/how_to_use?from=sp_globalfooter_cien"><span>即売会取り置きサイト</span>トリオキニ</a>
</li>
<li class="eisysGroupFooterService-link type-studio">
<a href="https://dlsitestudio.com/?from=sp_globalfooter_cien"><span>音声収録スタジオ</span>DLsiteスタジオ</a>
</li>
</ul>
</div>
</div>
</div>
<div class="snsArea">
<p class="heading">SNS公式アカウント</p>
<a href="https://twitter.com/cien_info?lang=ja" class="twitter_link" target="_blink" rel="nofollow noopener"></a>
</div>
<p class="copyright">&copy; 2018 Ci-en</p>
</div>
</footer>
<script src="https://ci-en.dlsite.com/assets/js/vendor.bundle.js?1568167511"></script>
<script src="https://ci-en.dlsite.com/assets/js/app.bundle.js?1568167511"></script>
</body>
</html>

View File

@@ -3097,7 +3097,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3:
dependencies:
ms "2.0.0"
debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.5:
debug@^3.1.0, debug@^3.1.1, debug@^3.2.5:
version "3.2.6"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
integrity sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==
@@ -3358,9 +3358,9 @@ domutils@^1.5.1, domutils@^1.7.0:
domelementtype "1"
dot-prop@^4.1.1:
version "4.2.0"
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.0.tgz#1f19e0c2e1aa0e32797c49799f2837ac6af69c57"
integrity sha512-tUMXrxlExSW6U2EXiiKGSBVdYgtV8qlHL+C10TsW4PURY/ic+eaysnSkwB4kA/mBlCyy/IKDJ+Lc3wbWeaXtuQ==
version "4.2.1"
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.2.1.tgz#45884194a71fc2cda71cbb4bceb3a4dd2f433ba4"
integrity sha512-l0p4+mIuJIua0mhxGoh4a+iNL9bmeK5DvnSVQa6T0OhrVmaEa1XScX5Etc673FePCJOArq/4Pa2cLGODUWTPOQ==
dependencies:
is-obj "^1.0.0"
@@ -3774,15 +3774,10 @@ etag@~1.8.1:
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
eventemitter3@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.0.tgz#d65176163887ee59f386d64c82610b696a4a74eb"
integrity sha512-qerSRB0p+UDEssxTtm6EDKcE7W4OaoisfIMl4CngyEhjpYglocpNg6UEqCvemdGhosAsg4sO2dXJOdyBifPGCg==
eventemitter3@^4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.4.tgz#b5463ace635a083d018bdc7c917b4c5f10a85384"
integrity sha512-rlaVLnVxtxvoyLsQQFBx53YmXHDxRIzzTLbdfxqi4yocpSjAxXwkU0cScM5JgSKMqEhrZpnvQ2D9gjylR0AimQ==
eventemitter3@^4.0.0, eventemitter3@^4.0.4:
version "4.0.7"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
events@^3.0.0:
version "3.1.0"
@@ -4158,11 +4153,9 @@ fn-name@~2.0.1:
integrity sha1-UhTXU3pNBqSjAcDMJi/rhBiAAuc=
follow-redirects@^1.0.0:
version "1.9.1"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.9.1.tgz#26e329669886f8b3baccbaf352067b0253da6c2d"
integrity sha512-oUNbrdUjHItyCytZQrHxWQ81ebL4xCFLH10sG0poUMgbKWoBnswpICjUBld3PLJ1lF6cCYVUBG7hAdLro0JNvg==
dependencies:
debug "^3.0.0"
version "1.13.0"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db"
integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==
for-in@^1.0.2:
version "1.0.2"
@@ -4746,9 +4739,9 @@ http-proxy-middleware@0.19.1:
micromatch "^3.1.10"
http-proxy@^1.17.0:
version "1.18.0"
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.0.tgz#dbe55f63e75a347db7f3d99974f2692a314a6a3a"
integrity sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==
version "1.18.1"
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"
integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==
dependencies:
eventemitter3 "^4.0.0"
follow-redirects "^1.0.0"