diff --git a/.dockerignore b/.dockerignore index e47e3356b..70376cdf4 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,6 @@ -storage data Dockerfile +contrib/docker/Dockerfile.* docker-compose*.yml .dockerignore .git diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 5a04578c1..000000000 --- a/Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -FROM php:7.2.6-fpm-alpine - -ARG COMPOSER_VERSION="1.6.5" -ARG COMPOSER_CHECKSUM="67bebe9df9866a795078bb2cf21798d8b0214f2e0b2fd81f2e907a8ef0be3434" - -RUN apk add --no-cache --virtual .build build-base autoconf imagemagick-dev libtool && \ - apk --no-cache add imagemagick git && \ - docker-php-ext-install pdo_mysql pcntl bcmath && \ - pecl install imagick && \ - docker-php-ext-enable imagick pcntl imagick && \ - curl -LsS https://getcomposer.org/download/${COMPOSER_VERSION}/composer.phar -o /tmp/composer.phar && \ - echo "${COMPOSER_CHECKSUM} /tmp/composer.phar" | sha256sum -c - && \ - install -m0755 -o root -g root /tmp/composer.phar /usr/bin/composer.phar && \ - ln -sf /usr/bin/composer.phar /usr/bin/composer && \ - rm /tmp/composer.phar && \ - apk --no-cache del --purge .build - -COPY . /var/www/html/ - -WORKDIR /var/www/html -RUN install -d -m0755 -o www-data -g www-data \ - /var/www/html/storage \ - /var/www/html/storage/framework \ - /var/www/html/storage/logs \ - /var/www/html/storage/framework/sessions \ - /var/www/html/storage/framework/views \ - /var/www/html/storage/framework/cache && \ - composer install --prefer-source --no-interaction - -VOLUME ["/var/www/html"] -ENV PATH="~/.composer/vendor/bin:./vendor/bin:${PATH}" diff --git a/Dockerfile b/Dockerfile new file mode 120000 index 000000000..2f722aa37 --- /dev/null +++ b/Dockerfile @@ -0,0 +1 @@ +contrib/docker/Dockerfile.apache \ No newline at end of file diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php index f9ba3b648..5217a9925 100644 --- a/app/Http/Controllers/Auth/RegisterController.php +++ b/app/Http/Controllers/Auth/RegisterController.php @@ -65,7 +65,7 @@ class RegisterController extends Controller ]; $rules = [ - 'name' => 'required|string|max:255', + 'name' => 'required|string|max' . config('pixelfed.max_name_length'), 'username' => $usernameRules, 'email' => 'required|string|email|max:255|unique:users', 'password' => 'required|string|min:6|confirmed', diff --git a/app/Http/Controllers/SettingsController.php b/app/Http/Controllers/SettingsController.php index ab8f35ce9..14eced67c 100644 --- a/app/Http/Controllers/SettingsController.php +++ b/app/Http/Controllers/SettingsController.php @@ -30,8 +30,8 @@ class SettingsController extends Controller public function homeUpdate(Request $request) { $this->validate($request, [ - 'name' => 'required|string|max:30', - 'bio' => 'nullable|string|max:125', + 'name' => 'required|string|max:' . config('pixelfed.max_name_length'), + 'bio' => 'nullable|string|max:' . config('pixelfed.max_bio_length') 'website' => 'nullable|url', 'email' => 'nullable|email' ]); diff --git a/app/Http/Controllers/StatusController.php b/app/Http/Controllers/StatusController.php index 57a7e41c1..6a9299939 100644 --- a/app/Http/Controllers/StatusController.php +++ b/app/Http/Controllers/StatusController.php @@ -21,17 +21,33 @@ class StatusController extends Controller ->withCount(['likes', 'comments', 'media']) ->findOrFail($id); - if(!$status->media_path && $status->in_reply_to_id) { - return redirect($status->url()); - } - if($request->wantsJson() && config('pixelfed.activitypub_enabled')) { return $this->showActivityPub($request, $status); } + $template = $this->detectTemplate($status); + $replies = Status::whereInReplyToId($status->id)->simplePaginate(30); - return view('status.show', compact('user', 'status', 'replies')); + return view($template, compact('user', 'status', 'replies')); + } + + protected function detectTemplate($status) + { + $template = Cache::rememberForever('template:status:type:'.$status->id, function () use($status) { + $template = 'status.show.photo'; + if(!$status->media_path && $status->in_reply_to_id) { + $template = 'status.reply'; + } + if($status->media->count() > 1) { + $template = 'status.show.album'; + } + if($status->viewType() == 'video') { + $template = 'status.show.video'; + } + return $template; + }); + return $template; } public function compose() @@ -42,11 +58,7 @@ class StatusController extends Controller public function store(Request $request) { - if(Auth::check() == false) - { - abort(403); - } - + $this->authCheck(); $user = Auth::user(); $size = Media::whereUserId($user->id)->sum('size') / 1000; @@ -56,7 +68,7 @@ class StatusController extends Controller } $this->validate($request, [ - 'photo.*' => 'required|mimes:jpeg,png,bmp,gif|max:' . config('pixelfed.max_photo_size'), + 'photo.*' => 'required|mimes:jpeg,png,gif|max:' . config('pixelfed.max_photo_size'), 'caption' => 'string|max:' . config('pixelfed.max_caption_length'), 'cw' => 'nullable|string', 'filter_class' => 'nullable|string', @@ -83,11 +95,13 @@ class StatusController extends Controller foreach ($photos as $k => $v) { $storagePath = "public/m/{$monthHash}/{$userHash}"; $path = $v->store($storagePath); + $hash = \hash_file('sha256', $v); $media = new Media; $media->status_id = $status->id; $media->profile_id = $profile->id; $media->user_id = $user->id; $media->media_path = $path; + $media->original_sha256 = $hash; $media->size = $v->getClientSize(); $media->mime = $v->getClientMimeType(); $media->filter_class = $request->input('filter_class'); @@ -172,6 +186,57 @@ class StatusController extends Controller return response(json_encode($res['data']))->header('Content-Type', 'application/activity+json'); } + public function edit(Request $request, $username, $id) + { + $this->authCheck(); + $user = Auth::user()->profile; + $status = Status::whereProfileId($user->id) + ->with(['media']) + ->findOrFail($id); + return view('status.edit', compact('user', 'status')); + } + + + public function editStore(Request $request, $username, $id) + { + $this->authCheck(); + $user = Auth::user()->profile; + $status = Status::whereProfileId($user->id) + ->with(['media']) + ->findOrFail($id); + + $this->validate($request, [ + 'id' => 'required|integer|min:1', + 'caption' => 'nullable', + 'filter' => 'nullable|alpha_dash|max:30' + ]); + + $id = $request->input('id'); + $caption = $request->input('caption'); + $filter = $request->input('filter'); + + $media = Media::whereProfileId($user->id) + ->whereStatusId($status->id) + ->find($id); + + $changed = false; + + if($media->caption != $caption) { + $media->caption = $caption; + $changed = true; + } + + if($media->filter_class != $filter) { + $media->filter_class = $filter; + $changed = true; + } + + if($changed === true) { + $media->save(); + } + return response()->json([], 200); + } + protected function authCheck() { if(Auth::check() == false) diff --git a/app/Http/Controllers/StoryController.php b/app/Http/Controllers/StoryController.php new file mode 100644 index 000000000..aafaa107e --- /dev/null +++ b/app/Http/Controllers/StoryController.php @@ -0,0 +1,11 @@ +profile->id; // TODO: Use redis for timelines - $following = Follower::whereProfileId(Auth::user()->profile->id)->pluck('following_id'); - $following->push(Auth::user()->profile->id); + $following = Follower::whereProfileId($pid)->pluck('following_id'); + $following->push($pid); + $filtered = UserFilter::whereUserId($pid) + ->whereFilterableType('App\Profile') + ->whereIn('filter_type', ['mute', 'block']) + ->pluck('filterable_id'); $timeline = Status::whereIn('profile_id', $following) + ->whereNotIn('profile_id', $filtered) ->orderBy('id','desc') ->withCount(['comments', 'likes']) ->simplePaginate(20); @@ -30,8 +36,18 @@ class TimelineController extends Controller { // TODO: Use redis for timelines // $timeline = Timeline::build()->local(); + $pid = Auth::user()->profile->id; + + $filtered = UserFilter::whereUserId($pid) + ->whereFilterableType('App\Profile') + ->whereIn('filter_type', ['mute', 'block']) + ->pluck('filterable_id'); + $private = Profile::whereIsPrivate(true)->pluck('id'); + $filtered = $filtered->merge($private); $timeline = Status::whereHas('media') + ->whereNotIn('profile_id', $filtered) ->whereNull('in_reply_to_id') + ->whereNull('reblog_of_id') ->withCount(['comments', 'likes']) ->orderBy('id','desc') ->simplePaginate(20); diff --git a/app/Jobs/ImageOptimizePipeline/ImageUpdate.php b/app/Jobs/ImageOptimizePipeline/ImageUpdate.php index 5b21ea7e9..482176bec 100644 --- a/app/Jobs/ImageOptimizePipeline/ImageUpdate.php +++ b/app/Jobs/ImageOptimizePipeline/ImageUpdate.php @@ -16,6 +16,12 @@ class ImageUpdate implements ShouldQueue protected $media; + protected $protectedMimes = [ + 'image/gif', + 'image/bmp', + 'video/mp4' + ]; + /** * Create a new job instance. * @@ -37,9 +43,9 @@ class ImageUpdate implements ShouldQueue $path = storage_path('app/'. $media->media_path); $thumb = storage_path('app/'. $media->thumbnail_path); try { - ImageOptimizer::optimize($thumb); - if($media->mime !== 'image/gif') + if(!in_array($media->mime, $this->protectedMimes)) { + ImageOptimizer::optimize($thumb); ImageOptimizer::optimize($path); } } catch (Exception $e) { diff --git a/app/Jobs/RemoteFollowPipeline/RemoteFollowImportRecent.php b/app/Jobs/RemoteFollowPipeline/RemoteFollowImportRecent.php index 1cb6a7828..9341babcb 100644 --- a/app/Jobs/RemoteFollowPipeline/RemoteFollowImportRecent.php +++ b/app/Jobs/RemoteFollowPipeline/RemoteFollowImportRecent.php @@ -13,6 +13,7 @@ use Illuminate\Queue\InteractsWithQueue; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Foundation\Bus\Dispatchable; use App\Jobs\StatusPipeline\NewStatusPipeline; +use App\Jobs\ImageOptimizePipeline\ImageThumbnail; class RemoteFollowImportRecent implements ShouldQueue { @@ -216,7 +217,9 @@ class RemoteFollowImportRecent implements ShouldQueue $media->size = 0; $media->mime = $mime; $media->save(); - + + ImageThumbnail::dispatch($media); + return true; } catch (Exception $e) { return false; diff --git a/app/Profile.php b/app/Profile.php index c620582b1..4487f81b6 100644 --- a/app/Profile.php +++ b/app/Profile.php @@ -143,7 +143,11 @@ class Profile extends Model public function statusCount() { - return $this->statuses()->whereHas('media')->count(); + return $this->statuses() + ->whereHas('media') + ->whereNull('in_reply_to_id') + ->whereNull('reblog_of_id') + ->count(); } public function recommendFollowers() @@ -159,6 +163,7 @@ class Profile extends Model ->whereNotIn('following_id', $follows) ->whereIn('profile_id', $following) ->orderByRaw('rand()') + ->distinct('id') ->limit(3) ->pluck('following_id'); $recommended = []; diff --git a/app/Report.php b/app/Report.php index 879a8df36..495f400ed 100644 --- a/app/Report.php +++ b/app/Report.php @@ -6,6 +6,8 @@ use Illuminate\Database\Eloquent\Model; class Report extends Model { + protected $dates = ['admin_seen']; + public function url() { return url('/i/admin/reports/show/' . $this->id); diff --git a/app/Util/ActivityPub/Concern/HTTPSignature.php b/app/Util/ActivityPub/Concern/HTTPSignature.php new file mode 100644 index 000000000..aa4f1fe9a --- /dev/null +++ b/app/Util/ActivityPub/Concern/HTTPSignature.php @@ -0,0 +1,94 @@ +is_url === false) { + return true; + } + + $url = $this->profile; + try { + $url = filter_var($url, FILTER_VALIDATE_URL); + $parsed = parse_url($url, PHP_URL_HOST); + if(!$parsed || in_array($parsed, $this->localhosts)) { + return false; + } + } catch (Exception $e) { + return false; + } + return true; + } + + public function fetchPublicKey($profile, bool $is_url = true) + { + $this->profile = $profile; + $this->is_url = $is_url; + $valid = $this->validateUrl(); + if(!$valid) { + throw new \Exception('Invalid URL provided'); + } + if($is_url && isset($profile->public_key) && $profile->public_key) { + return $profile->public_key; + } + + try { + $url = $this->profile; + $res = Zttp::timeout(30)->withHeaders([ + 'Accept' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', + 'User-Agent' => 'PixelFedBot v0.1 - https://pixelfed.org' + ])->get($url); + $actor = json_decode($res->getBody(), true); + } catch (Exception $e) { + throw new Exception('Unable to fetch public key'); + } + + return $actor['publicKey']['publicKeyPem']; + } + + public function sendSignedObject($senderProfile, $url, $body) + { + $profile = $senderProfile; + $context = new Context([ + 'keys' => [$profile->keyId() => $profile->private_key], + 'algorithm' => 'rsa-sha256', + 'headers' => ['(request-target)', 'Date'], + ]); + + $handlerStack = GuzzleHttpSignatures::defaultHandlerFromContext($context); + $client = new Client(['handler' => $handlerStack]); + + $headers = [ + 'Accept' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', + 'Date' => date('D, d M Y h:i:s') . ' GMT', + 'Content-Type' => 'application/activity+json', + 'User-Agent' => 'PixelFedBot - https://pixelfed.org' + ]; + + $response = $client->post($url, [ + 'options' => [ + 'allow_redirects' => false, + 'verify' => true, + 'timeout' => 30 + ], + 'headers' => $headers, + 'body' => $body + ]); + + return $response->getBody(); + } + + +} \ No newline at end of file diff --git a/config/pixelfed.php b/config/pixelfed.php index b33c6c885..b675f4080 100644 --- a/config/pixelfed.php +++ b/config/pixelfed.php @@ -108,6 +108,26 @@ return [ */ 'max_caption_length' => env('MAX_CAPTION_LENGTH', 500), + /* + |-------------------------------------------------------------------------- + | Bio length limit + |-------------------------------------------------------------------------- + | + | Change the bio length limit for user profiles. + | + */ + 'max_bio_length' => env('MAX_BIO_LENGTH', 125), + + /* + |-------------------------------------------------------------------------- + | User name length limit + |-------------------------------------------------------------------------- + | + | Change the length limit for user names. + | + */ + 'max_name_length' => env('MAX_NAME_LENGTH', 30), + /* |-------------------------------------------------------------------------- | Album size limit @@ -138,4 +158,4 @@ return [ */ 'image_quality' => (int) env('IMAGE_QUALITY', 80), -]; \ No newline at end of file +]; diff --git a/config/trustedproxy.php b/config/trustedproxy.php new file mode 100644 index 000000000..0a0776ada --- /dev/null +++ b/config/trustedproxy.php @@ -0,0 +1,27 @@ +getClientIp() + * always gets the originating client IP, no matter + * how many proxies that client's request has + * subsequently passed through. + */ + 'proxies' => explode(',', env('TRUST_PROXIES', '')), +]; diff --git a/contrib/docker/Dockerfile.apache b/contrib/docker/Dockerfile.apache new file mode 100644 index 000000000..481db4647 --- /dev/null +++ b/contrib/docker/Dockerfile.apache @@ -0,0 +1,59 @@ +FROM php:7-apache + +ARG COMPOSER_VERSION="1.6.5" +ARG COMPOSER_CHECKSUM="67bebe9df9866a795078bb2cf21798d8b0214f2e0b2fd81f2e907a8ef0be3434" + +RUN apt-get update \ + && apt-get install -y --no-install-recommends git \ + optipng pngquant jpegoptim gifsicle \ + libfreetype6 libjpeg62-turbo libpng16-16 libxpm4 libvpx4 libmagickwand-6.q16-3 \ + libfreetype6-dev libjpeg62-turbo-dev libpng-dev libxpm-dev libvpx-dev libmagickwand-dev \ + && docker-php-source extract \ + && docker-php-ext-configure gd \ + --with-freetype-dir=/usr/lib/x86_64-linux-gnu/ \ + --with-jpeg-dir=/usr/lib/x86_64-linux-gnu/ \ + --with-xpm-dir=/usr/lib/x86_64-linux-gnu/ \ + --with-vpx-dir=/usr/lib/x86_64-linux-gnu/ \ + && docker-php-ext-install pdo_mysql pcntl gd exif bcmath \ + && pecl install imagick \ + && docker-php-ext-enable imagick pcntl imagick gd exif \ + && a2enmod rewrite \ + && curl -LsS https://getcomposer.org/download/${COMPOSER_VERSION}/composer.phar -o /usr/bin/composer \ + && echo "${COMPOSER_CHECKSUM} /usr/bin/composer" | sha256sum -c - \ + && chmod 755 /usr/bin/composer \ + && apt-get autoremove --purge -y \ + libfreetype6-dev libjpeg62-turbo-dev libpng-dev libxpm-dev libvpx-dev libmagickwand-dev \ + && rm -rf /var/cache/apt \ + && docker-php-source delete + +ENV PATH="~/.composer/vendor/bin:./vendor/bin:${PATH}" + +COPY . /var/www/ + +WORKDIR /var/www/ +RUN cp -r storage storage.skel \ + && cp contrib/docker/php.ini /usr/local/etc/php/conf.d/pixelfed.ini \ + && composer install --prefer-source --no-interaction \ + && rm -rf html && ln -s public html + +VOLUME ["/var/www/storage"] + +ENV APP_ENV=production \ + APP_DEBUG=false \ + LOG_CHANNEL=stderr \ + DB_CONNECTION=mysql \ + DB_PORT=3306 \ + DB_HOST=db \ + BROADCAST_DRIVER=log \ + QUEUE_DRIVER=redis \ + HORIZON_PREFIX=horizon-pixelfed \ + REDIS_HOST=redis \ + SESSION_SECURE_COOKIE=true \ + API_BASE="/api/1/" \ + API_SEARCH="/api/search" \ + OPEN_REGISTRATION=true \ + ENFORCE_EMAIL_VERIFICATION=true \ + REMOTE_FOLLOW=false \ + ACTIVITY_PUB=false + +CMD /var/www/contrib/docker/start.sh diff --git a/contrib/docker/Dockerfile.fpm b/contrib/docker/Dockerfile.fpm new file mode 100644 index 000000000..c9ee294a0 --- /dev/null +++ b/contrib/docker/Dockerfile.fpm @@ -0,0 +1,31 @@ +FROM php:7.2.6-fpm-alpine + +ARG COMPOSER_VERSION="1.6.5" +ARG COMPOSER_CHECKSUM="67bebe9df9866a795078bb2cf21798d8b0214f2e0b2fd81f2e907a8ef0be3434" + +RUN apk add --no-cache --virtual .build build-base autoconf imagemagick-dev libtool && \ + apk --no-cache add imagemagick git && \ + docker-php-ext-install pdo_mysql pcntl && \ + pecl install imagick && \ + docker-php-ext-enable imagick pcntl imagick && \ + curl -LsS https://getcomposer.org/download/${COMPOSER_VERSION}/composer.phar -o /tmp/composer.phar && \ + echo "${COMPOSER_CHECKSUM} /tmp/composer.phar" | sha256sum -c - && \ + install -m0755 -o root -g root /tmp/composer.phar /usr/bin/composer.phar && \ + ln -sf /usr/bin/composer.phar /usr/bin/composer && \ + rm /tmp/composer.phar && \ + apk --no-cache del --purge .build + +COPY . /var/www/html/ + +WORKDIR /var/www/html +RUN install -d -m0755 -o www-data -g www-data \ + /var/www/html/storage \ + /var/www/html/storage/framework \ + /var/www/html/storage/logs \ + /var/www/html/storage/framework/sessions \ + /var/www/html/storage/framework/views \ + /var/www/html/storage/framework/cache && \ + composer install --prefer-source --no-interaction + +VOLUME ["/var/www/html"] +ENV PATH="~/.composer/vendor/bin:./vendor/bin:${PATH}" diff --git a/contrib/docker/php.ini b/contrib/docker/php.ini new file mode 100644 index 000000000..7fc73c567 --- /dev/null +++ b/contrib/docker/php.ini @@ -0,0 +1,5 @@ +file_uploads = On +memory_limit = 64M +upload_max_filesize = 64M +post_max_size = 64M +max_execution_time = 600 diff --git a/contrib/docker/start.sh b/contrib/docker/start.sh new file mode 100755 index 000000000..be374a914 --- /dev/null +++ b/contrib/docker/start.sh @@ -0,0 +1,17 @@ +#!/bin/bash + +# Create the storage tree if needed and fix permissions +cp -r storage.skel/* storage/ +chown -R www-data:www-data storage/ +php artisan storage:link + +# Migrate database if the app was upgraded +php artisan migrate --force + +# Run a worker if it is set as embedded +if [ HORIZON_EMBED = true ]; then + php artisan horizon & +fi + +# Finally run Apache +exec apache2-foreground diff --git a/database/migrations/2018_08_22_022306_update_settings_table.php b/database/migrations/2018_08_22_022306_update_settings_table.php index 500612903..d6d198cc8 100644 --- a/database/migrations/2018_08_22_022306_update_settings_table.php +++ b/database/migrations/2018_08_22_022306_update_settings_table.php @@ -28,9 +28,11 @@ class UpdateSettingsTable extends Migration */ public function down() { - $table->dropColumn('show_profile_followers'); - $table->dropColumn('show_profile_follower_count'); - $table->dropColumn('show_profile_following'); - $table->dropColumn('show_profile_following_count'); + Schema::table('user_settings', function (Blueprint $table) { + $table->dropColumn('show_profile_followers'); + $table->dropColumn('show_profile_follower_count'); + $table->dropColumn('show_profile_following'); + $table->dropColumn('show_profile_following_count'); + }); } } diff --git a/database/migrations/2018_08_27_004653_update_media_table_add_alt_text.php b/database/migrations/2018_08_27_004653_update_media_table_add_alt_text.php new file mode 100644 index 000000000..2071ba3b1 --- /dev/null +++ b/database/migrations/2018_08_27_004653_update_media_table_add_alt_text.php @@ -0,0 +1,44 @@ +string('original_sha256')->nullable()->index()->after('user_id'); + $table->string('optimized_sha256')->nullable()->index()->after('original_sha256'); + $table->string('caption')->nullable()->after('thumbnail_url'); + $table->string('hls_path')->nullable()->after('caption'); + $table->timestamp('hls_transcoded_at')->nullable()->after('processed_at'); + $table->string('key')->nullable(); + $table->json('metadata')->nullable(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('media', function (Blueprint $table) { + $table->dropColumn('original_sha256'); + $table->dropColumn('optimized_sha256'); + $table->dropColumn('caption'); + $table->dropColumn('hls_path'); + $table->dropColumn('hls_transcoded_at'); + $table->dropColumn('key'); + $table->dropColumn('metadata'); + }); + } +} diff --git a/docker-compose.yml b/docker-compose.yml index 861dc0b9d..4c6e8f6db 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,49 +1,56 @@ --- version: '3' -services: - nginx: - image: nginx:alpine - networks: - - internal - - external - ports: - - 3000:80 - volumes: - - "php-storage:/var/www/html" - - ./contrib/nginx.conf:/etc/nginx/conf.d/default.conf - depends_on: - - php - php: - build: . +# In order to set configuration, please use a .env file in +# your compose project directory (the same directory as your +# docker-compose.yml), and set database options, application +# name, key, and other settings there. +# A list of available settings is available in .env.example +# +# The services should scale properly across a swarm cluster +# if the volumes are properly shared between cluster members. + +services: + + app: + # Uncomment to build a local copy of the image + # build: . image: pixelfed - volumes: - - "php-storage:/var/www/html" - networks: - - internal - environment: - - DB_HOST=mysql - - DB_DATABASE=pixelfed - - DB_USERNAME=${DB_USERNAME:-pixelfed} - - DB_PASSWORD=${DB_PASSWORD:-pixelfed} - - REDIS_HOST=redis - - APP_KEY=${APP_KEY} + # If you have a traefik running, uncomment this to expose Pixelfed + # labels: + # - traefik.enable=true + # - traefik.frontend.rule=Host:your.url + # - traefik.port=80 env_file: - ./.env + volumes: + - "app-storage:/var/www/storage" + networks: + - external + - internal - mysql: + # Uncomment if you set HORIZON_EMBED to false and wish to run a local worker + # worker: + # image: pixelfed + # env_file: + # - ./.env + # volumes: + # - "app-storage:/var/www/storage" + # networks: + # - internal + # command: php artisan horizon + + db: image: mysql:5.7 networks: - internal environment: - MYSQL_DATABASE=pixelfed - - MYSQL_USER=${DB_USERNAME:-pixelfed} - - MYSQL_PASSWORD=${DB_PASSWORD:-pixelfed} - - MYSQL_RANDOM_ROOT_PASSWORD="true" - env_file: - - ./.env + - MYSQL_USER=${DB_USERNAME} + - MYSQL_PASSWORD=${DB_PASSWORD} + - MYSQL_RANDOM_ROOT_PASSWORD=true volumes: - - "mysql-data:/var/lib/mysql" + - "db-data:/var/lib/mysql" redis: image: redis:4-alpine @@ -52,10 +59,11 @@ services: networks: - internal +# Adjust your volume data in order to store data where you wish volumes: redis-data: - mysql-data: - php-storage: + db-data: + app-storage: networks: internal: diff --git a/resources/assets/sass/custom.scss b/resources/assets/sass/custom.scss index 5aa67fb47..90dbb8bbe 100644 --- a/resources/assets/sass/custom.scss +++ b/resources/assets/sass/custom.scss @@ -298,3 +298,13 @@ details summary::-webkit-details-marker { .profile-avatar img { object-fit: cover; } + +.tt-menu { + padding: 0 !important; + border-radius: 0 0 0.25rem 0.25rem !important; +} + +.tt-dataset .alert { + border: 0 !important; + border-radius: 0 !important; +} diff --git a/resources/lang/en/profile.php b/resources/lang/en/profile.php index 9fb9d10db..24278aba8 100644 --- a/resources/lang/en/profile.php +++ b/resources/lang/en/profile.php @@ -6,4 +6,7 @@ return [ 'emptyFollowing' => 'This user is not following anyone yet!', 'emptySaved' => 'You haven’t saved any post yet!', 'savedWarning' => 'Only you can see what you’ve saved', + 'privateProfileWarning' => 'This Account is Private', + 'alreadyFollow' => 'Already follow :username?', + 'loginToSeeProfile' => 'to see their photos and videos.', ]; diff --git a/resources/views/account/verify_email.blade.php b/resources/views/account/verify_email.blade.php index 99ae26f3c..f79cdfc5f 100644 --- a/resources/views/account/verify_email.blade.php +++ b/resources/views/account/verify_email.blade.php @@ -5,7 +5,12 @@
{{ session('status') }}
+{{ session('error') }}
# | +Reporter | +Type | +Reported | +Status | +Created | +|
---|---|---|---|---|---|---|
+ + {{$report->id}} + + | +{{$report->reporter->username}} | +{{$report->type}} | +{{str_limit($report->reported()->url(), 25)}} | + @if(!$report->admin_seen) +Unresolved | + @else +Resolved | + @endif +{{$report->created_at->diffForHumans(null, true, true, true)}} | +
+ Message: + {{$report->message ?? 'No message provided.'}} +
+ + @if(!$report->admin_seen) + Ignore + {{-- Request Mod Feedback --}} + Add CW + Unlist/Hide + Delete + Shadowban User + Ban User + @else +Resolved {{$report->admin_seen->diffForHumans()}}
+ @endif +{{$file->getFilename()}}
++ + Size: {{App\Util\Lexer\PrettyNumber::convert($file->getSize())}} + + + Created: {{\Carbon\Carbon::createFromTimestamp($file->getMTime())->diffForHumans()}}
+ + +{{config('pixelfed.version')}}
+{{DB::select( DB::raw("select version()") )[0]->{'version()'} }}
+{{phpversion()}}
++ {{$user->name}} + @if($user->remote_url) + REMOTE PROFILE + @endif +
+{{$user->bio}}
+ +{{$user->name}} diff --git a/resources/views/profile/private.blade.php b/resources/views/profile/private.blade.php new file mode 100644 index 000000000..dda823e33 --- /dev/null +++ b/resources/views/profile/private.blade.php @@ -0,0 +1,33 @@ +@extends('layouts.app',['title' => $user->username . " on " . config('app.name')]) + +@section('content') + +@include('profile.partial.private-info') + +
+ {{__('profile.privateProfileWarning')}} +
+ + @if(Auth::check()) +{{ __('profile.alreadyFollow', ['username'=>$user->username])}}
+ +{{__('profile.loginToSeeProfile')}}
+ @endif ++ Learn more + about our reporting guidelines and policy.
++ Learn more + about our reporting guidelines and policy.
++ Learn more + about our reporting guidelines and policy.
+This feature is not yet ready for production. Please try again later.
-Please select one of the following options.
+Please select one of the following options.
Learn more
diff --git a/resources/views/report/not-interested.blade.php b/resources/views/report/not-interested.blade.php
index 687a19751..2b32daa0a 100644
--- a/resources/views/report/not-interested.blade.php
+++ b/resources/views/report/not-interested.blade.php
@@ -12,6 +12,11 @@
You can unfollow or mute a user or hashtag from appearing in your timeline. Unless the content violates our terms of service, there is nothing we can do to remove it.
+ Learn more
+ about our reporting guidelines and policy.
+ Learn more + about our reporting guidelines and policy.
++ Learn more + about our reporting guidelines and policy.
++ Learn more + about our reporting guidelines and policy.
+Please select one of the following options.
Please select one of the following options.
-- This comment contains spam -
+Learn more about our reporting guidelines and policy.
diff --git a/resources/views/report/spam/profile.blade.php b/resources/views/report/spam/profile.blade.php index 9e4a36e6f..6ee85c67d 100644 --- a/resources/views/report/spam/profile.blade.php +++ b/resources/views/report/spam/profile.blade.php @@ -28,7 +28,7 @@ This users profile contains spamLearn more about our reporting guidelines and policy.
diff --git a/resources/views/settings/home.blade.php b/resources/views/settings/home.blade.php index 4c43b306d..18f78e3f1 100644 --- a/resources/views/settings/home.blade.php +++ b/resources/views/settings/home.blade.php @@ -39,6 +39,9 @@CW / NSFW / Hidden Media
+(click to show)
+CW / NSFW / Hidden Media
+(click to show)
++ {{$status->profile->username}} + {!! $status->rendered ?? e($status->caption) !!} +
+ ++ {{ str_limit($item->profile->username, 15)}} + {!! $item->rendered ?? e($item->caption) !!} {{$item->created_at->diffForHumans(null, true, true ,true)}} +
+ @endforeach +CW / NSFW / Hidden Media
+(click to show)
+