&& rm -rf /tmp/* /var/cache/apk/*
COPY Gemfile Gemfile.lock package.json yarn.lock .yarnclean /mastodon/
+COPY stack-fix.c /lib
+RUN gcc -shared -fPIC /lib/stack-fix.c -o /lib/stack-fix.so
+RUN rm /lib/stack-fix.c
- RUN bundle config build.nokogiri --with-iconv-lib=/usr/local/lib --with-iconv-include=/usr/local/include \
+ RUN bundle config build.nokogiri --use-system-libraries --with-iconv-lib=/usr/local/lib --with-iconv-include=/usr/local/include \
&& bundle install -j$(getconf _NPROCESSORS_ONLN) --deployment --without test development \
&& yarn install --pure-lockfile --ignore-engines \
&& yarn cache clean
before_action :set_body_classes
before_action :set_user, only: [:finish_signup]
+ before_action :set_pack
- # GET/PATCH /users/:id/finish_signup
def finish_signup
return unless request.patch? && params[:user]
+
if @user.update(user_params)
@user.skip_reconfirmation!
bypass_sign_in(@user)
include Attachmentable
include Paginable
include AccountCounters
+ include DomainNormalizable
+ MAX_DISPLAY_NAME_LENGTH = (ENV['MAX_DISPLAY_NAME_CHARS'] || 30).to_i
+ MAX_NOTE_LENGTH = (ENV['MAX_BIO_CHARS'] || 500).to_i
+ MAX_FIELDS = (ENV['MAX_PROFILE_FIELDS'] || 4).to_i
+
enum protocol: [:ostatus, :activitypub]
validates :username, presence: true
media = validate_media!(options[:media_ids])
status = nil
- text = options.delete(:spoiler_text) if text.blank? && options[:spoiler_text].present?
+ if text.blank? && options[:spoiler_text].present?
+ text = '.'
+ text = media.find(&:video?) ? '馃摴' : '馃柤' if media.size > 0
+ end
+ visibility = options[:visibility] || account.user&.setting_default_privacy
+ visibility = :unlisted if visibility == :public && account.silenced
+
ApplicationRecord.transaction do
status = account.statuses.create!(text: text,
media_attachments: media || [],
LinkCrawlWorker.perform_async(status.id) unless status.spoiler_text?
DistributionWorker.perform_async(status.id)
- Pubsubhubbub::DistributionWorker.perform_async(status.stream_entry.id)
- ActivityPub::DistributionWorker.perform_async(status.id)
+
+ unless status.local_only?
+ Pubsubhubbub::DistributionWorker.perform_async(status.stream_entry.id)
+ ActivityPub::DistributionWorker.perform_async(status.id)
- ActivityPub::ReplyDistributionWorker.perform_async(status.id) if status.reply? && status.thread.account.local?
+ end
if options[:idempotency].present?
redis.setex("idempotency:status:#{account.id}:#{options[:idempotency]}", 3_600, status.id)
phrase: Zostanie wykryte nawet, gdy znajduje si臋 za ostrze偶eniem o zawarto艣ci
scopes: Wyb贸r API, do kt贸rych aplikacja b臋dzie mia艂a dost臋p. Je偶eli wybierzesz nadrz臋dny zakres, nie musisz wybiera膰 jego element贸w.
setting_default_language: J臋zyk Twoich wpis贸w mo偶e by膰 wykrywany automatycznie, ale nie zawsze jest to dok艂adne
+ setting_display_media_show_all: Zawsze pokazuj zawarto艣膰 multimedialn膮 jako wra偶liw膮
setting_hide_network: Informacje o tym, kto Ci臋 艣ledzi i kogo 艣ledzisz nie b臋d膮 widoczne
setting_noindex: Wp艂ywa na widoczno艣膰 strony profilu i Twoich wpis贸w
- setting_theme: Zmienia wygl膮d Mastodona po zalogowaniu z dowolnego urz膮dzenia.
+ setting_skin: Zmienia wygl膮d u偶ywanej odmiany Mastodona
whole_word: Je艣li s艂owo lub fraza sk艂ada si臋 jedynie z liter lub cyfr, filtr b臋dzie zastosowany tylko do pe艂nych wyst膮pie艅
imports:
data: Plik CSV wyeksportowany z innej instancji Mastodona
setting_default_privacy: Widoczno艣膰 wpis贸w
setting_default_sensitive: Zawsze oznaczaj zawarto艣膰 multimedialn膮 jako wra偶liw膮
setting_delete_modal: Pytaj o potwierdzenie przed usuni臋ciem wpisu
+ setting_display_media_hide_all: Ukryj wszystko
+ setting_display_media_show_all: Poka偶 wszystko
+ setting_favourite_modal: Pytaj o potwierdzenie przed dodaniem do ulubionych
setting_hide_network: Ukryj swoj膮 sie膰
setting_noindex: Nie indeksuj mojego profilu w wyszukiwarkach internetowych
setting_reduce_motion: Ogranicz ruch w animacjach
resource :change_email, only: [:show, :update]
resource :reset, only: [:create]
resource :action, only: [:new, :create], controller: 'account_actions'
- resources :statuses, only: [:index, :create, :update, :destroy]
+ resources :statuses, only: [:index, :show, :create, :update, :destroy]
+ resources :followers, only: [:index]
resource :confirmation, only: [:create] do
collection do
{
loader: 'sass-loader',
options: {
+ includePaths: ['app/javascript'],
+ fiber: require('fibers'),
+ implementation: require('sass'),
+ sourceMap: true,
},
},
],
const { basename, dirname, join, relative, resolve } = require('path');
const { sync } = require('glob');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
- const ManifestPlugin = require('webpack-manifest-plugin');
+ const AssetsManifestPlugin = require('webpack-assets-manifest');
-const extname = require('path-complete-extname');
-const { env, settings, themes, output, loadersDir } = require('./configuration.js');
-const localePackPaths = require('./generateLocalePacks');
+const { env, settings, core, flavours, output, loadersDir } = require('./configuration.js');
+const localePacks = require('./generateLocalePacks');
+
+function reducePacks (data, into = {}) {
+ if (!data.pack) {
+ return into;
+ }
+ Object.keys(data.pack).reduce((map, entry) => {
+ const pack = data.pack[entry];
+ if (!pack) {
+ return map;
+ }
+ const packFile = typeof pack === 'string' ? pack : pack.filename;
+ if (packFile) {
+ map[data.name ? `flavours/${data.name}/${entry}` : `core/${entry}`] = resolve(data.pack_directory, packFile);
+ }
+ return map;
+ }, into);
+ if (data.name) {
+ Object.keys(data.skin).reduce((map, entry) => {
+ const skin = data.skin[entry];
+ const skinName = entry;
+ if (!skin) {
+ return map;
+ }
+ Object.keys(skin).reduce((map, entry) => {
+ const packFile = skin[entry];
+ if (!packFile) {
+ return map;
+ }
+ map[`skins/${data.name}/${skinName}/${entry}`] = resolve(packFile);
+ return map;
+ }, into);
+ return map;
+ }, into);
+ }
+ return into;
+}
+
+const entries = Object.assign(
+ { locales: resolve('app', 'javascript', 'locales') },
+ localePacks,
+ reducePacks(core),
+ Object.keys(flavours).reduce((map, entry) => reducePacks(flavours[entry], map), {})
+);
-const extensionGlob = `**/*{${settings.extensions.join(',')}}*`;
-const entryPath = join(settings.source_path, settings.source_entry_path);
-const packPaths = sync(join(entryPath, extensionGlob));
module.exports = {
- entry: Object.assign(
- packPaths.reduce((map, entry) => {
- const localMap = map;
- const namespace = relative(join(entryPath), dirname(entry));
- localMap[join(namespace, basename(entry, extname(entry)))] = resolve(entry);
- return localMap;
- }, {}),
- localePackPaths.reduce((map, entry) => {
- const localMap = map;
- localMap[basename(entry, extname(entry, extname(entry)))] = resolve(entry);
- return localMap;
- }, {}),
- Object.keys(themes).reduce((themePaths, name) => {
- themePaths[name] = resolve(join(settings.source_path, themes[name]));
- return themePaths;
- }, {})
- ),
+ entry: entries,
output: {
filename: '[name].js',
],
"private": true,
"dependencies": {
- "@babel/core": "^7.0.1",
- "@babel/plugin-proposal-class-properties": "^7.0.0",
- "@babel/plugin-proposal-decorators": "^7.0.0",
- "@babel/plugin-proposal-object-rest-spread": "^7.0.0",
- "@babel/plugin-syntax-dynamic-import": "^7.0.0",
- "@babel/plugin-transform-react-inline-elements": "^7.0.0",
- "@babel/plugin-transform-react-jsx-self": "^7.0.0",
- "@babel/plugin-transform-react-jsx-source": "^7.0.0",
- "@babel/plugin-transform-runtime": "^7.0.0",
- "@babel/preset-env": "^7.0.0",
+ "@babel/core": "^7.2.2",
+ "@babel/plugin-proposal-class-properties": "^7.2.3",
+ "@babel/plugin-proposal-decorators": "^7.2.3",
+ "@babel/plugin-proposal-object-rest-spread": "^7.2.0",
+ "@babel/plugin-syntax-dynamic-import": "^7.2.0",
+ "@babel/plugin-transform-react-inline-elements": "^7.2.0",
+ "@babel/plugin-transform-react-jsx-self": "^7.2.0",
+ "@babel/plugin-transform-react-jsx-source": "^7.2.0",
+ "@babel/plugin-transform-runtime": "^7.2.0",
+ "@babel/preset-env": "^7.2.3",
"@babel/preset-react": "^7.0.0",
- "@babel/runtime": "^7.0.0",
+ "@babel/runtime": "^7.2.0",
+ "@gfx/zopfli": "^1.0.10",
"array-includes": "^3.0.3",
- "autoprefixer": "^8.6.5",
- "axios": "~0.16.2",
+ "atrament": "^0.2.3",
+ "autoprefixer": "^9.4.3",
+ "axios": "^0.18.0",
"babel-core": "^7.0.0-bridge.0",
- "babel-loader": "^8.0.2",
+ "babel-loader": "^8.0.4",
"babel-plugin-lodash": "^3.3.4",
"babel-plugin-preval": "^3.0.1",
- "babel-plugin-react-intl": "^3.0.0",
- "babel-plugin-transform-react-remove-prop-types": "^0.4.15",
+ "babel-plugin-react-intl": "^3.0.1",
+ "babel-plugin-transform-react-remove-prop-types": "^0.4.21",
+ "babel-runtime": "^6.26.0",
"classnames": "^2.2.5",
"compression-webpack-plugin": "^2.0.0",
"cross-env": "^5.1.4",
"es6-symbol": "^3.1.1",
"escape-html": "^1.0.3",
"exif-js": "^2.3.0",
- "express": "^4.16.2",
+ "express": "^4.16.4",
+ "favico.js": "^0.3.10",
- "file-loader": "^2.0.0",
+ "fibers": "^3.1.1",
+ "file-loader": "^3.0.1",
"font-awesome": "^4.7.0",
"glob": "^7.1.1",
"history": "^4.7.2",
describe StatusLengthValidator do
describe '#validate' do
- it 'does not add errors onto remote statuses'
- it 'does not add errors onto local reblogs'
+ it 'does not add errors onto remote statuses' do
+ status = double(local?: false)
+ subject.validate(status)
+ expect(status).not_to receive(:errors)
+ end
+
+ it 'does not add errors onto local reblogs' do
+ status = double(local?: false, reblog?: true)
+ subject.validate(status)
+ expect(status).not_to receive(:errors)
+ end
- it 'adds an error when content warning is over 500 characters' do
- status = double(spoiler_text: 'a' * 520, text: '', errors: double(add: nil), local?: true, reblog?: false)
+ it 'adds an error when content warning is over MAX_CHARS characters' do
+ chars = StatusLengthValidator::MAX_CHARS + 1
+ status = double(spoiler_text: 'a' * chars, text: '', errors: double(add: nil), local?: true, reblog?: false)
subject.validate(status)
expect(status.errors).to have_received(:add)
end
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
- autoprefixer@^8.6.5:
- version "8.6.5"
- resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-8.6.5.tgz#343f3d193ed568b3208e00117a1b96eb691d4ee9"
- integrity sha512-PLWJN3Xo/rycNkx+mp8iBDMTm3FeWe4VmYaZDSqL5QQB9sLsQkG5k8n+LNDFnhh9kdq2K+egL/icpctOmDHwig==
+atrament@^0.2.3:
+ version "0.2.3"
+ resolved "https://registry.yarnpkg.com/atrament/-/atrament-0.2.3.tgz#6ccbc0daa6d3f25e5aeaeb31befeb78e86980348"
+
+ autoprefixer@^9.4.3:
+ version "9.4.3"
+ resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.4.3.tgz#c97384a8fd80477b78049163a91bbc725d9c41d9"
+ integrity sha512-/XSnzDepRkAU//xLcXA/lUWxpsBuw0WiriAHOqnxkuCtzLhaz+fL4it4gp20BQ8n5SyLzK/FOc7A0+u/rti2FQ==
dependencies:
- browserslist "^3.2.8"
- caniuse-lite "^1.0.30000864"
+ browserslist "^4.3.6"
+ caniuse-lite "^1.0.30000921"
normalize-range "^0.1.2"
num2fraction "^1.2.2"
- postcss "^6.0.23"
- postcss-value-parser "^3.2.3"
-
- aws-sign2@~0.6.0:
- version "0.6.0"
- resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f"
- integrity sha1-FDQt0428yU0OW4fXY81jYSwOeU8=
+ postcss "^7.0.6"
+ postcss-value-parser "^3.3.1"
aws-sign2@~0.7.0:
version "0.7.0"
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
fastparse@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8"
- integrity sha1-0eJkOzipTXWDtHkGDmxK/8lAcfg=
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9"
+ integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==
+favico.js@^0.3.10:
+ version "0.3.10"
+ resolved "https://registry.yarnpkg.com/favico.js/-/favico.js-0.3.10.tgz#80586e27a117f24a8d51c18a99bdc714d4339301"
+
faye-websocket@^0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4"