├── .gitignore ├── readme.md ├── js ├── admin │ ├── package.json │ ├── Gulpfile.js │ ├── src │ │ ├── main.js │ │ ├── addImageUploadPane.js │ │ └── components │ │ │ └── ImageUploadPage.js │ └── dist │ │ └── extension.js └── forum │ ├── package.json │ ├── Gulpfile.js │ ├── src │ ├── main.js │ └── components │ │ └── UploadButton.js │ └── dist │ └── extension.js ├── less ├── admin │ └── settingsPage.less └── forum │ └── upload.less ├── src ├── Validators │ └── ImageValidator.php ├── Image.php ├── Api │ ├── Serializers │ │ └── ImageSerializer.php │ └── Controllers │ │ └── UploadImageController.php ├── Listeners │ ├── DeleteImage.php │ ├── AddUploadsApi.php │ ├── AddClientAssets.php │ └── LoadSettingsFromDatabase.php ├── Contracts │ └── UploadAdapterContract.php ├── Events │ └── ImageWillBeSaved.php ├── Commands │ ├── UploadImage.php │ └── UploadImageHandler.php ├── Adapters │ ├── CloudinaryAdapter.php │ ├── LocalAdapter.php │ └── ImgurAdapter.php └── Providers │ └── StorageServiceProvider.php ├── bootstrap.php ├── changelog.md ├── locale ├── cn.yml ├── nl.yml ├── ru.yml ├── en.yml ├── tr.yml ├── de.yml ├── it.yml └── fr.yml ├── license.md ├── migrations ├── 2016_01_13_000000_alter_flagrow_images_table.php └── 2016_01_11_000000_create_flagrow_images_table.php └── composer.json /.gitignore: -------------------------------------------------------------------------------- 1 | js/*/node_modules 2 | vendor/ 3 | composer.lock -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Obsolete, use https://github.com/flagrow/upload instead, it does everything this extension does and way more! 2 | -------------------------------------------------------------------------------- /js/admin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "devDependencies": { 4 | "gulp": "^3.8.11", 5 | "flarum-gulp": "^0.2.0" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /js/forum/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "devDependencies": { 4 | "gulp": "^3.8.11", 5 | "flarum-gulp": "^0.2.0" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /js/admin/Gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('flarum-gulp'); 2 | 3 | gulp({ 4 | modules: { 5 | 'flagrow/image-upload': [ 6 | 'src/**/*.js' 7 | ] 8 | } 9 | }); -------------------------------------------------------------------------------- /js/forum/Gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('flarum-gulp'); 2 | 3 | gulp({ 4 | modules: { 5 | 'flagrow/image-upload': [ 6 | 'src/**/*.js' 7 | ] 8 | } 9 | }); -------------------------------------------------------------------------------- /less/admin/settingsPage.less: -------------------------------------------------------------------------------- 1 | .ImageUploadPage { 2 | padding: 20px 0; 3 | 4 | @media @desktop-up { 5 | .container { 6 | max-width: 600px; 7 | margin: 0; 8 | } 9 | 10 | fieldset { 11 | margin-bottom: 30px; 12 | 13 | > ul { 14 | list-style: none; 15 | margin: 0; 16 | padding: 0; 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /js/admin/src/main.js: -------------------------------------------------------------------------------- 1 | import { extend } from 'flarum/extend'; 2 | import app from 'flarum/app'; 3 | import saveSettings from 'flarum/utils/saveSettings'; 4 | import PermissionGrid from 'flarum/components/PermissionGrid'; 5 | 6 | import addImageUploadPane from 'flagrow/image-upload/addImageUploadPane' 7 | 8 | app.initializers.add('flagrow-image-upload', app => { 9 | // add the admin pane 10 | addImageUploadPane(); 11 | 12 | // add the permission option to the relative pane 13 | extend(PermissionGrid.prototype, 'startItems', items => { 14 | items.add('uploadImages', { 15 | icon: 'picture-o', 16 | label: app.translator.trans('flagrow-image-upload.admin.permissions.upload_images_label'), 17 | permission: 'flagrow.image.upload' 18 | }); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /src/Validators/ImageValidator.php: -------------------------------------------------------------------------------- 1 | [ 28 | 'required', 29 | 'image', 30 | 'max:' . $this->maxFileSize 31 | ] 32 | ]; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Image.php: -------------------------------------------------------------------------------- 1 | subscribe(Listeners\AddClientAssets::class); 23 | // add attributes to API 24 | $events->subscribe(Listeners\LoadSettingsFromDatabase::class); 25 | // registers the API endpoint and the permission send to the API 26 | $events->subscribe(Listeners\AddUploadsApi::class); 27 | 28 | // register the service provider 29 | $app->register(StorageServiceProvider::class); 30 | }; 31 | -------------------------------------------------------------------------------- /src/Api/Serializers/ImageSerializer.php: -------------------------------------------------------------------------------- 1 | $model->file_url, 35 | 'path' => $model->file_name, 36 | 'size' => $model->file_size 37 | ]; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /less/forum/upload.less: -------------------------------------------------------------------------------- 1 | .flagrow-image-upload-button { 2 | overflow: hidden; 3 | position: relative; 4 | 5 | input { 6 | position: absolute; 7 | top: 0; 8 | right: 0; 9 | margin: 0; 10 | padding: 0; 11 | font-size: 20px; 12 | cursor: pointer; 13 | opacity: 0; 14 | filter: alpha(opacity=0); 15 | } 16 | } 17 | 18 | .fa-spin { 19 | -webkit-animation: fa-spin 2s infinite linear; 20 | animation: fa-spin 2s infinite linear; 21 | } 22 | 23 | .green { 24 | color: rgb(46, 204, 113) !important; 25 | } 26 | 27 | .red { 28 | color: rgb(242, 38, 19) !important; 29 | } 30 | @-webkit-keyframes fa-spin { 31 | 0% { 32 | -webkit-transform: rotate(0deg); 33 | transform: rotate(0deg); 34 | } 35 | 100% { 36 | -webkit-transform: rotate(359deg); 37 | transform: rotate(359deg); 38 | } 39 | } 40 | @keyframes fa-spin { 41 | 0% { 42 | -webkit-transform: rotate(0deg); 43 | transform: rotate(0deg); 44 | } 45 | 100% { 46 | -webkit-transform: rotate(359deg); 47 | transform: rotate(359deg); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /js/admin/src/addImageUploadPane.js: -------------------------------------------------------------------------------- 1 | import { extend } from 'flarum/extend'; 2 | import AdminNav from 'flarum/components/AdminNav'; 3 | import AdminLinkButton from 'flarum/components/AdminLinkButton'; 4 | 5 | import ImageUploadPage from 'flagrow/image-upload/components/ImageUploadPage'; 6 | 7 | export default function() { 8 | // create the route 9 | app.routes['image-upload'] = {path: '/image-upload', component: ImageUploadPage.component()}; 10 | 11 | // bind the route we created to the three dots settings button 12 | app.extensionSettings['flagrow-image-upload'] = () => m.route(app.route('image-upload')); 13 | 14 | extend(AdminNav.prototype, 'items', items => { 15 | // add the Image Upload tab to the admin navigation menu 16 | items.add('image-upload', AdminLinkButton.component({ 17 | href: app.route('image-upload'), 18 | icon: 'picture-o', 19 | children: 'Image Upload', 20 | description: app.translator.trans('flagrow-image-upload.admin.help_texts.description') 21 | })); 22 | }); 23 | } 24 | -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # 0.2.0 2 | 3 | - permission to allow uploading only to a specific group 4 | - requires flarum/composer-installer 0.3.0 or higher 5 | - requires flarum/flarum-ext-markdown 0.1.0-beta.3 or higher 6 | - moved logic of uploading to php 7 | - added/restored imgur and local adapters 8 | - now validates image uploads 9 | - added cloudinary.com adapter 10 | - updated readme to reflect Flagrow skeleton 11 | 12 | # 0.1.4 13 | 14 | - added chinese locale 15 | 16 | # 0.1.3 17 | 18 | - removed composer.lock 19 | - applied styleci fixes adhering to PSR-2 20 | - prefixed package with flarum-ext- as per convention 21 | - fixed duplicate copyright headers generated by styleci 22 | - fixed subfolder installation not providing proper link to locally stored image 23 | - fixed migration being broken due to renaming package 24 | 25 | # 0.1.2 26 | 27 | - fixed double slashes in local storage uploads 28 | 29 | # 0.1.1 30 | 31 | - fix support for https for both imgur and local storage modes 32 | 33 | # 0.1.0 34 | 35 | - support imgur anonymous mode; oauth adapter 36 | - support local storage mode; requires api endpoint 37 | -------------------------------------------------------------------------------- /src/Listeners/DeleteImage.php: -------------------------------------------------------------------------------- 1 | listen(PostWasDeleted::class, [$this, 'postWasDeleted']); 29 | } 30 | 31 | /** 32 | * Deletes any images that were listed for that post. 33 | * 34 | * @param PostWasDeleted $event 35 | */ 36 | public function postWasDeleted(PostWasDeleted $event) 37 | { 38 | Image::where('post_id', $event->post->id)->delete(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /locale/cn.yml: -------------------------------------------------------------------------------- 1 | flagrow-image-upload: 2 | forum: 3 | buttons: 4 | attach: 选择 5 | states: 6 | loading: 加载 7 | success: 成功 8 | error: 错误 9 | admin: 10 | upload_methods: 11 | local: 本地 12 | imgur: Imgur 13 | labels: 14 | upload_method: 上传方式 15 | resize: 16 | title: 图像尺寸 17 | toggle: 是否裁剪尺寸 18 | max_width: 最大宽度 19 | max_height: 最大高度 20 | imgur: 21 | title: Imgur 设置 22 | client_id: Client-ID 23 | local: 24 | title: 本地存储设置 25 | cdn_url: CDN 地址前缀 26 | buttons: 27 | save: 保存 28 | help_texts: 29 | description: > 30 | 设置图像上传服务和首选项。 31 | upload_method: > 32 | 在这里您可以选择要使用的图像上传方式。 33 | 当你选择一个方式后,下面会显示相应的设置项。 34 | resize: > 35 | 在图像上传之前,是否要调整图像的大小。 36 | 你可以设置一个最大宽度和高度,以像素为单位。 37 | 在调整图像尺寸的过程中会保持原图像的长宽比。 38 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Flagrow 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /js/forum/src/main.js: -------------------------------------------------------------------------------- 1 | import { extend } from 'flarum/extend'; 2 | import TextEditor from 'flarum/components/TextEditor'; 3 | 4 | import UploadButton from 'flagrow/image-upload/components/UploadButton'; 5 | 6 | app.initializers.add('flagrow-image-upload', app => { 7 | 8 | /** 9 | * Add the upload button to the post composer. 10 | */ 11 | extend(TextEditor.prototype, 'controlItems', function(items) 12 | { 13 | // check whether the user can upload images. If not, returns. 14 | if (!app.forum.attribute('canUploadImages')) return; 15 | 16 | // create and add the button 17 | var uploadButton = new UploadButton; 18 | uploadButton.textAreaObj = this; 19 | items.add('flagrow-image-upload', uploadButton, 0); 20 | 21 | // animate the button on hover: shows the label 22 | $('.Button-label', '.item-flagrow-image-upload > div').hide(); 23 | $('.item-flagrow-image-upload > div').hover( 24 | function(){ $('.Button-label', this).show(); $(this).removeClass('Button--icon')}, 25 | function(){ $('.Button-label', this).hide(); $(this).addClass('Button--icon')} 26 | ); 27 | }); 28 | }); 29 | -------------------------------------------------------------------------------- /src/Contracts/UploadAdapterContract.php: -------------------------------------------------------------------------------- 1 | function (Builder $schema) { 19 | $schema->table('flagrow_images', function (Blueprint $table) { 20 | // the specific post id this image is appearing in 21 | $table->dropColumn('post_id'); 22 | // file url of the image 23 | $table->string('file_url')->nullable(); 24 | // file size of the image 25 | $table->integer('file_size')->default(0); 26 | }); 27 | }, 28 | 'down' => function (Builder $schema) { 29 | $schema->table('flagrow_images', function (Blueprint $table) { 30 | $table->dropColumn('file_url'); 31 | $table->dropColumn('file_size'); 32 | $table->integer('post_id')->unsigned(); 33 | }); 34 | } 35 | ]; 36 | -------------------------------------------------------------------------------- /src/Events/ImageWillBeSaved.php: -------------------------------------------------------------------------------- 1 | actor = $actor; 52 | $this->image = $image; 53 | $this->body = $file; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /migrations/2016_01_11_000000_create_flagrow_images_table.php: -------------------------------------------------------------------------------- 1 | function (Builder $schema) { 19 | $schema->create('flagrow_images', function (Blueprint $table) { 20 | $table->increments('id'); 21 | 22 | // the user who posted the image 23 | $table->integer('user_id')->unsigned()->nullable(); 24 | 25 | // the specific post id this image is appearing in 26 | $table->integer('post_id')->unsigned(); 27 | 28 | // file name of the image 29 | $table->string('file_name')->nullable(); 30 | 31 | // the method this file was uploaded to, allows for future erasing on remote systems 32 | $table->string('upload_method'); 33 | 34 | // adds created_at 35 | $table->timestamp('created_at'); 36 | }); 37 | }, 38 | 'down' => function (Builder $schema) { 39 | $schema->drop('flagrow_images'); 40 | } 41 | ]; 42 | -------------------------------------------------------------------------------- /src/Commands/UploadImage.php: -------------------------------------------------------------------------------- 1 | postId = $postId; 49 | $this->file = $file; 50 | $this->actor = $actor; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flagrow/flarum-ext-image-upload", 3 | "description": "Image uploader for Flarum forum messages.", 4 | "keywords": ["upload", "flarum", "flagrow", "image", "imgur"], 5 | "type": "flarum-extension", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "Matteo Pompili", 10 | "email": "matpompili@gmail.com" 11 | }, 12 | { 13 | "name": "Daniël Klabbers", 14 | "email": "daniel+flarum@klabbers.email", 15 | "homepage": "http://hyn.io" 16 | } 17 | ], 18 | "support": { 19 | "issues": "https://github.com/flagrow/flarum-ext-image-upload/issues", 20 | "source": "https://github.com/flagrow/flarum-ext-image-upload" 21 | }, 22 | "require": { 23 | "flarum/core": "^0.1.0-beta.6", 24 | "flarum/flarum-ext-markdown": "^0.1.0-beta.3" 25 | }, 26 | "suggest": { 27 | "cloudinary/cloudinary_php": "Allows uploading images to cloudinary." 28 | }, 29 | "extra": { 30 | "flarum-extension": { 31 | "title": "Image Upload", 32 | "icon": { 33 | "name": "upload", 34 | "backgroundColor": "#000", 35 | "color": "#87d37c" 36 | } 37 | }, 38 | "branch-alias": { 39 | "dev-master": "0.3.x-dev" 40 | } 41 | }, 42 | "autoload": { 43 | "psr-4": { 44 | "Flagrow\\ImageUpload\\": "src/" 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /locale/nl.yml: -------------------------------------------------------------------------------- 1 | flagrow-image-upload: 2 | forum: 3 | buttons: 4 | attach: Attach 5 | states: 6 | loading: Laad 7 | success: Succes 8 | error: Fout 9 | admin: 10 | permissions: 11 | upload_images_label: Upload plaatjes 12 | upload_methods: 13 | local: Lokaal 14 | imgur: Imgur 15 | labels: 16 | upload_method: Upload methode 17 | preferences: 18 | title: Algemene instellingen 19 | max_file_size: Maximale bestandsgrootte (in kilobytes) 20 | resize: 21 | title: Verkleinen 22 | toggle: Schakel verkleining in 23 | max_width: Maximale breedte plaatje 24 | max_height: Maximale hoogte plaatje 25 | imgur: 26 | title: Imgur settings 27 | client_id: Client-ID 28 | local: 29 | title: Lokale opslag instellingen 30 | cdn_url: Content Delivery URL (prefixed de bestandsnaam) 31 | buttons: 32 | save: Opslaan 33 | help_texts: 34 | description: > 35 | Configureer upload diensten en voorkeuren. 36 | upload_method: > 37 | Selecteer de upload methode. Bij het kiezen van een 38 | specifieke waarde zullen meer opties getoond worden. 39 | resize: > 40 | Bepaal of je de plaatjes wilt verkleinen voordat 41 | ze geupload worden. Je kan een een maximale hoogte 42 | en breedte bepalen in pixels. Het verkleiningsproces 43 | houdt rekening met de hoogte/breedte verhouding. 44 | -------------------------------------------------------------------------------- /src/Listeners/AddUploadsApi.php: -------------------------------------------------------------------------------- 1 | listen(ConfigureApiRoutes::class, [$this, 'configureApiRoutes']); 31 | $events->listen(PrepareApiAttributes::class, [$this, 'prepareApiAttributes']); 32 | } 33 | 34 | /** 35 | * Registers our routes. 36 | * 37 | * @param ConfigureApiRoutes $event 38 | */ 39 | public function configureApiRoutes(ConfigureApiRoutes $event) 40 | { 41 | $event->post('/image/upload', 'flagrow.image.upload', UploadImageController::class); 42 | } 43 | 44 | /** 45 | * Gets the api attributes and makes them available to the forum. 46 | * 47 | * @param PrepareApiAttributes $event 48 | */ 49 | public function prepareApiAttributes(PrepareApiAttributes $event) 50 | { 51 | if ($event->isSerializer(ForumSerializer::class)) { 52 | $event->attributes['canUploadImages'] = $event->actor->can('flagrow.image.upload'); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Api/Controllers/UploadImageController.php: -------------------------------------------------------------------------------- 1 | bus = $bus; 44 | } 45 | /** 46 | * Get the data to be serialized and assigned to the response document. 47 | * 48 | * @param ServerRequestInterface $request 49 | * @param Document $document 50 | * @return mixed 51 | */ 52 | protected function data(ServerRequestInterface $request, Document $document) 53 | { 54 | $postId = array_get($request->getQueryParams(), 'post'); 55 | $actor = $request->getAttribute('actor'); 56 | $file = array_get($request->getUploadedFiles(), 'image'); 57 | 58 | return $this->bus->dispatch( 59 | new UploadImage($postId, $file, $actor) 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /locale/ru.yml: -------------------------------------------------------------------------------- 1 | flagrow-image-upload: 2 | forum: 3 | buttons: 4 | attach: Прикрепить 5 | states: 6 | loading: Загрузка 7 | success: Загружено 8 | error: Ошибка 9 | admin: 10 | permissions: 11 | upload_images_label: Загрузка изображений 12 | upload_methods: 13 | local: Локально 14 | imgur: Imgur 15 | cloudinary: Cloudinary 16 | labels: 17 | upload_method: Метод загрузки 18 | preferences: 19 | title: Общее 20 | max_file_size: Максимальный размер файла (в килобайтах) 21 | resize: 22 | title: Размер изображения 23 | toggle: Разрешить изменение изображения 24 | max_width: Максимальная ширина 25 | max_height: Максимальная высота 26 | imgur: 27 | title: Настройки Imgur 28 | client_id: Client-ID 29 | local: 30 | title: Настройки локального хранилища 31 | cdn_url: Конечный URL (префикс файла) 32 | cloudinary: 33 | title: Настройки Cloudinary 34 | cloud_name: Cloud name 35 | api_key: Api key 36 | api_secret: Api secret 37 | buttons: 38 | save: Сохранить настройки 39 | help_texts: 40 | description: > 41 | Настройка загрузки изображений. 42 | upload_method: > 43 | Здесь вы можете выбрать метод, который вы хотите использовать 44 | для загрузки изображений. 45 | resize: > 46 | Выберите, нужно ли изменить размер изображения перед загрузкой изображения. 47 | Вы можете выбрать максимальную ширину 48 | и высоту в пикселях. Процесс изменения размера сохраняет пропорции изображения. 49 | -------------------------------------------------------------------------------- /locale/en.yml: -------------------------------------------------------------------------------- 1 | flagrow-image-upload: 2 | forum: 3 | buttons: 4 | attach: Attach 5 | states: 6 | loading: Loading 7 | success: Success 8 | error: Error 9 | admin: 10 | permissions: 11 | upload_images_label: Upload images 12 | upload_methods: 13 | local: Local 14 | imgur: Imgur 15 | cloudinary: Cloudinary 16 | labels: 17 | upload_method: Upload method 18 | preferences: 19 | title: General preferences 20 | max_file_size: Maximum file size (in kilobytes) 21 | resize: 22 | title: Image resize 23 | toggle: Toggle the resize 24 | max_width: Maximum image width 25 | max_height: Maximum image height 26 | imgur: 27 | title: Imgur settings 28 | client_id: Client-ID 29 | local: 30 | title: Local storage settings 31 | cdn_url: Content Delivery URL (prefixes files) 32 | cloudinary: 33 | title: Cloudinary settings 34 | cloud_name: Cloud name 35 | api_key: Api key 36 | api_secret: Api secret 37 | buttons: 38 | save: Save settings 39 | help_texts: 40 | description: > 41 | Set up image uploading services and preferences. 42 | upload_method: > 43 | Here you can select the method that you want to use 44 | to upload your images. When you select one the 45 | relative setting card is going to be displayed. 46 | resize: > 47 | Choose whether you want to resize your images before 48 | they get uploaded. You can choose a maximum width 49 | and height, in pixels. The resizing process keeps the 50 | aspect ratio of the images. 51 | -------------------------------------------------------------------------------- /src/Adapters/CloudinaryAdapter.php: -------------------------------------------------------------------------------- 1 | write($name, $contents); 47 | 48 | $meta = Uploader::upload(storage_path('tmp/' . $name)); 49 | // force secure url 50 | $meta['url'] = $meta['secure_url']; 51 | return $meta; 52 | } 53 | 54 | 55 | public function uploadFile($name, $file) 56 | { 57 | // TODO: Implement uploadFile() method. 58 | } 59 | 60 | /** 61 | * Delete a remote file based on a adapter identifier. 62 | * 63 | * @param string $name 64 | * @param string $file 65 | * @return bool 66 | */ 67 | public function deleteFile($name, $file) 68 | { 69 | // TODO: Implement deleteFile() method. 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /locale/tr.yml: -------------------------------------------------------------------------------- 1 | flagrow-image-upload: 2 | forum: 3 | buttons: 4 | attach: Ekle 5 | states: 6 | loading: Yükleniyor 7 | success: Başarılı 8 | error: Hata 9 | admin: 10 | permissions: 11 | upload_images_label: Resim yükleme 12 | upload_methods: 13 | local: Yerel 14 | imgur: Imgur 15 | cloudinary: Cloudinary 16 | labels: 17 | upload_method: Yükleme yöntemi 18 | preferences: 19 | title: Genel özellikler 20 | max_file_size: Maksimum dosya boyutu (kilobayt cinsinden) 21 | resize: 22 | title: Yeniden boyutlandırma 23 | toggle: Yeniden boyutlandırmaya geç 24 | max_width: Maksimum resim genişliği 25 | max_height: Maksimum resim yüksekliği 26 | imgur: 27 | title: Imgur ayarları 28 | client_id: Client-ID 29 | local: 30 | title: Yerel depolama ayarları 31 | cdn_url: Content Delivery URL (prefixes files) 32 | cloudinary: 33 | title: Cloudinary ayarları 34 | cloud_name: Cloud ismi 35 | api_key: Api anahtarı 36 | api_secret: Api secret 37 | buttons: 38 | save: Ayarları kaydet 39 | help_texts: 40 | description: > 41 | Resim yükleme servislerini ve ayarlarını yapın. 42 | upload_method: > 43 | Burada resimleri yüklerken kullanmak istediğiniz 44 | yöntemi seçebilirsiniz. Seçim yaptığınızda uygun 45 | ayar ekranı da görüntülenecek. 46 | resize: > 47 | Resimlerinizin yüklenmeden önce yeniden boyutlandırılmasını 48 | isteyip istemediğinizi seçin. Maksimum yükseklik ve genişliği 49 | piksel cinsinden seçebilirsiniz. Bu işlem resimlerin yükseklik 50 | ve genişlik oranını koruyacaktır. 51 | -------------------------------------------------------------------------------- /locale/de.yml: -------------------------------------------------------------------------------- 1 | flagrow-image-upload: 2 | forum: 3 | buttons: 4 | attach: Einfügen 5 | states: 6 | loading: Bitte warten ... 7 | success: Erfolg 8 | error: Fehler 9 | admin: 10 | permissions: 11 | upload_images_label: Bilder hochladen 12 | upload_methods: 13 | local: Lokal 14 | imgur: Imgur 15 | cloudinary: Cloudinary 16 | labels: 17 | upload_method: Speicher Methode 18 | preferences: 19 | title: Allgemeine Einstellungen 20 | max_file_size: Maximale Dateigröße (in Kilobytes) 21 | resize: 22 | title: Größe ändern 23 | toggle: Größe des Bildes ändern 24 | max_width: Maximale Bildbreite 25 | max_height: Maximale Bildhöhe 26 | imgur: 27 | title: Imgur Einstellungen 28 | client_id: Client-ID 29 | local: 30 | title: Lokale Speicherung Einstellungen 31 | cdn_url: Content Delivery URL (Prefix für Bilder) 32 | cloudinary: 33 | title: Cloudinary Einstellungen 34 | cloud_name: Cloud name 35 | api_key: API Key 36 | api_secret: API Secret 37 | buttons: 38 | save: => core.ref.save_changes 39 | help_texts: 40 | description: > 41 | Richte das Bilder hochladen und die Einstellungen ein. 42 | upload_method: > 43 | Hier kannst du die Methode aussuchen, mit der die Bilder 44 | hochgeladen werden sollen. Die entsprechenden Einstellungen werden danach angezeigt. 45 | resize: > 46 | Wähle aus, ob die Bilder in ihrer Größe vor dem Upload 47 | verändert werden sollen. Du kannst dabei eine maximale 48 | Breite und Höhe (in Pixel) einstellen. Bei der 49 | Größenveränderung wird das Seitenverhältnis beibehalten. 50 | -------------------------------------------------------------------------------- /locale/it.yml: -------------------------------------------------------------------------------- 1 | flagrow-image-upload: 2 | forum: 3 | buttons: 4 | attach: Allega 5 | states: 6 | loading: Caricando 7 | success: Perfetto 8 | error: Errore 9 | admin: 10 | permissions: 11 | upload_images_label: Caricare immagini 12 | upload_methods: 13 | local: Locale 14 | imgur: Imgur 15 | cloudinary: Cloudinary 16 | labels: 17 | upload_method: Metodo di caricamento 18 | preferences: 19 | title: Impostazioni generali 20 | max_file_size: Dimensione massima immagini (in KB) 21 | resize: 22 | title: Ridimensionamento dell'immagine 23 | toggle: Aziona il ridimensionamento 24 | max_width: Massima larghezza 25 | max_height: Massima altezza 26 | imgur: 27 | title: Impostazioni di Imgur 28 | client_id: Client-ID 29 | local: 30 | title: Impostazioni di caricamento locale 31 | cdn_url: Indirizzo CDN (prefisso per i file) 32 | cloudinary: 33 | title: Impostazioni di Cloudinary 34 | cloud_name: Cloud name 35 | api_key: Api key 36 | api_secret: Api secret 37 | buttons: 38 | save: Salva impostazioni 39 | help_texts: 40 | description: > 41 | Imposta i servizi e le preferenze dell'uploader. 42 | upload_method: > 43 | Da qui puoi selezionare il metodo che vuoi usare 44 | per caricare le tue immagini. Quando ne selezioni uno, 45 | verrà mostrato il relativo pannello delle impostazioni. 46 | resize: > 47 | Segli se vuoi ridimensionare o meno le immagini 48 | prima del loro caricamento. Puoi scelgiere l'altezza e la 49 | larghezza massime, in pixel. Il ridimensionamento mantiene 50 | il rapporto altezza:larghezza delle immagini. 51 | -------------------------------------------------------------------------------- /locale/fr.yml: -------------------------------------------------------------------------------- 1 | flagrow-image-upload: 2 | forum: 3 | buttons: 4 | attach: Ajouter une image 5 | states: 6 | loading: Envoi 7 | success: Terminé 8 | error: Erreur 9 | admin: 10 | permissions: 11 | upload_images_label: Ajouter des images 12 | upload_methods: 13 | local: Local 14 | imgur: Imgur 15 | cloudinary: Cloudinary 16 | labels: 17 | upload_method: Méthode d'envoi 18 | preferences: 19 | title: Préférences générales 20 | max_file_size: Taille d'image maximale (en kilooctets) 21 | resize: 22 | title: Redimensionnement d'image 23 | toggle: Basculer les redimensionnements 24 | max_width: Largeur maximale de l'image 25 | max_height: Hauteur maximale de l'image 26 | imgur: 27 | title: Paramètres Imgur 28 | client_id: Client-ID 29 | local: 30 | title: Paramètres de stockage local 31 | cdn_url: URL du réseau de diffusion de contenu (fichiers préfixes) 32 | cloudinary: 33 | title: Paramètres Cloudinary 34 | cloud_name: Nom du Cloud 35 | api_key: Clé Api 36 | api_secret: Secret Api 37 | buttons: 38 | save: Sauvegarder les paramètres 39 | help_texts: 40 | description: > 41 | Paramétrer les services d'envoi d'images et les préférences. 42 | upload_method: > 43 | Ici vous pouvez sélectionner la méthode que vous voulez utiliser 44 | pour envoyer les images. Quand vous en sélectionnez une, 45 | la carte de paramétrage correspondante sera affichée. 46 | resize: > 47 | Choisissez si vous voulez redimensionner vos images avant 48 | qu'elles soient envoyées. Vous pouvez choisir une largeur 49 | et une hauteur maximales, en pixels. Le processus de redimensionnement 50 | garde le rapport d'aspect des images. 51 | -------------------------------------------------------------------------------- /src/Adapters/LocalAdapter.php: -------------------------------------------------------------------------------- 1 | filesystem = $filesystem; 41 | $this->settings = $settings; 42 | } 43 | 44 | /** 45 | * Uploads raw contents to the service. 46 | * 47 | * @param string $contents 48 | * @return array The meta of the file. 49 | */ 50 | public function uploadContents($name, $contents) 51 | { 52 | $this->filesystem->write($name, $contents); 53 | $meta = $this->filesystem->getMetadata($name); 54 | 55 | $urlGenerator = app('Flarum\Forum\UrlGenerator'); 56 | 57 | if (empty($this->settings->get('flagrow.image-upload.cdnUrl'))) { // if there is no cdnUrl 58 | $meta['url'] = $urlGenerator->toPath('assets/images/' . $name); 59 | } else { // if there is 60 | $meta['url'] = $this->settings->get('flagrow.image-upload.cdnUrl') . 'assets/images/' . $name; 61 | } 62 | 63 | return $meta; 64 | } 65 | 66 | 67 | public function uploadFile($name, $file) 68 | { 69 | // TODO: Implement uploadFile() method. 70 | } 71 | 72 | /** 73 | * Delete a remote file based on a adapter identifier. 74 | * 75 | * @param string $name 76 | * @param string $file 77 | * @return bool 78 | */ 79 | public function deleteFile($name, $file) 80 | { 81 | // TODO: Implement deleteFile() method. 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Adapters/ImgurAdapter.php: -------------------------------------------------------------------------------- 1 | client = new Client([ 33 | 'base_uri' => 'https://api.imgur.com/3/', 34 | 'headers' => [ 35 | 'Authorization' => 'Client-ID ' . $clientId 36 | ] 37 | ]); 38 | } 39 | 40 | /** 41 | * Uploads raw contents to the service. 42 | * 43 | * @param string $name 44 | * @param string $contents 45 | * @return array The meta of the file. 46 | */ 47 | public function uploadContents($name, $contents) 48 | { 49 | $result = $this->client->post('upload', [ 50 | 'json' => [ 51 | 'image' => base64_encode($contents), 52 | 'type' => 'base64', 53 | ] 54 | ]); 55 | if ($result->getStatusCode() === 200) { 56 | $meta = array_get(json_decode($result->getBody(), true), 'data', []); 57 | $meta['url'] = array_get($meta, 'link'); 58 | $meta = preg_replace("/^http:/i", "https:", $meta); 59 | return $meta; 60 | } else { 61 | return false; 62 | } 63 | } 64 | 65 | /** 66 | * Uploads a local (tmp) file to the service. 67 | * 68 | * @param string $name 69 | * @param string $file 70 | * @return array The meta of the file. 71 | */ 72 | public function uploadFile($name, $file) 73 | { 74 | // TODO: Implement uploadFile() method. 75 | } 76 | 77 | /** 78 | * Delete a remote file based on a adapter identifier. 79 | * 80 | * @param string $name 81 | * @param string $file 82 | * @return bool 83 | */ 84 | public function deleteFile($name, $file) 85 | { 86 | // TODO: Implement deleteFile() method. 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Listeners/AddClientAssets.php: -------------------------------------------------------------------------------- 1 | listen(ConfigureClientView::class, [$this, 'addForumAssets']); 31 | $events->listen(ConfigureClientView::class, [$this, 'addAdminAssets']); 32 | $events->listen(ConfigureLocales::class, [$this, 'addLocales']); 33 | } 34 | 35 | /** 36 | * Modifies the client view for the Forum. 37 | * 38 | * @param ConfigureClientView $event 39 | */ 40 | public function addForumAssets(ConfigureClientView $event) 41 | { 42 | if ($event->isForum()) { 43 | $event->addAssets([ 44 | __DIR__ . '/../../less/forum/upload.less', 45 | __DIR__ . '/../../js/forum/dist/extension.js' 46 | ]); 47 | $event->addBootstrapper('flagrow/image-upload/main'); 48 | } 49 | } 50 | 51 | /** 52 | * Modifies the client view for the Admin. 53 | * 54 | * @param ConfigureClientView $event 55 | */ 56 | public function addAdminAssets(ConfigureClientView $event) 57 | { 58 | if ($event->isAdmin()) { 59 | $event->addAssets([ 60 | __DIR__ . '/../../less/admin/settingsPage.less', 61 | __DIR__ . '/../../js/admin/dist/extension.js' 62 | ]); 63 | $event->addBootstrapper('flagrow/image-upload/main'); 64 | } 65 | } 66 | 67 | /** 68 | * Provides i18n files. 69 | * 70 | * @param ConfigureLocales $event 71 | */ 72 | public function addLocales(ConfigureLocales $event) 73 | { 74 | foreach (new DirectoryIterator(__DIR__.'/../../locale') as $file) { 75 | if ($file->isFile() && in_array($file->getExtension(), ['yml', 'yaml'])) { 76 | $event->locales->addTranslations($file->getBasename('.'.$file->getExtension()), $file->getPathname()); 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/Providers/StorageServiceProvider.php: -------------------------------------------------------------------------------- 1 | instantiateUploadAdapter($app); 39 | }; 40 | 41 | $this->app->when(UploadImageHandler::class) 42 | ->needs(UploadAdapterContract::class) 43 | ->give($uploadAdapter); 44 | } 45 | 46 | /** 47 | * Sets the upload adapter for the specific preferred service. 48 | * 49 | * @param $app 50 | * @return FilesystemInterface 51 | */ 52 | protected function instantiateUploadAdapter($app) 53 | { 54 | $settings = $app->make('flarum.settings'); 55 | switch ($settings->get('flagrow.image-upload.uploadMethod', 'local')) { 56 | case 'imgur': 57 | return new ImgurAdapter($settings->get('flagrow.image-upload.imgurClientId')); 58 | break; 59 | case 'cloudinary': 60 | return new CloudinaryAdapter( 61 | $settings->get('flagrow.image-upload.cloudinaryCloudName'), 62 | $settings->get('flagrow.image-upload.cloudinaryApiKey'), 63 | $settings->get('flagrow.image-upload.cloudinaryApiSecret') 64 | ); 65 | break; 66 | default: 67 | return new LocalAdapter( 68 | new Filesystem(new Local(public_path('assets/images'))), 69 | $app->make(SettingsRepositoryInterface::class) 70 | ); 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/Listeners/LoadSettingsFromDatabase.php: -------------------------------------------------------------------------------- 1 | settings = $settings; 44 | } 45 | 46 | /** 47 | * Subscribes to the Flarum events. 48 | * 49 | * @param Dispatcher $events 50 | */ 51 | public function subscribe(Dispatcher $events) 52 | { 53 | $events->listen(PrepareUnserializedSettings::class, [$this, 'addUploadMethods']); 54 | $events->listen(PrepareApiAttributes::class, [$this, 'prepareApiAttributes']); 55 | } 56 | 57 | /** 58 | * Get the setting values from the database and make them available 59 | * in the forum. 60 | * 61 | * @param PrepareApiAttributes $event 62 | */ 63 | public function prepareApiAttributes(PrepareApiAttributes $event) 64 | { 65 | if ($event->isSerializer(ForumSerializer::class)) { 66 | foreach ($this->fieldsToGet as $field) { 67 | $event->attributes[$this->packagePrefix . $field] = $this->settings->get($this->packagePrefix . $field); 68 | } 69 | } 70 | } 71 | 72 | /** 73 | * Check for installed packages and provide the upload methods option 74 | * in the admin page- 75 | * 76 | * @param PrepareUnserializedSettings $event 77 | */ 78 | public function addUploadMethods(PrepareUnserializedSettings $event) 79 | { 80 | // these are the upload methods that doesn't require external libraries 81 | $methods = [ 82 | 'local', 83 | 'imgur' 84 | ]; 85 | 86 | // check for Cloudinary, if present add the method 87 | if (class_exists(Cloudinary::class)) { 88 | $methods[] = 'cloudinary'; 89 | } 90 | 91 | // add the methods with the relative translations 92 | $event->settings['flagrow.image-upload.availableUploadMethods'] = []; 93 | foreach ($methods as $method) { 94 | $event->settings['flagrow.image-upload.availableUploadMethods'][$method] = app('translator')->trans('flagrow-image-upload.admin.upload_methods.' . $method); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /js/forum/src/components/UploadButton.js: -------------------------------------------------------------------------------- 1 | import Component from 'flarum/Component'; 2 | import icon from 'flarum/helpers/icon'; 3 | import LoadingIndicator from 'flarum/components/LoadingIndicator'; 4 | 5 | export default class UploadButton extends Component { 6 | 7 | /** 8 | * Load the configured remote uploader service. 9 | */ 10 | init() { 11 | // the service type handling uploads 12 | this.textAreaObj = null; 13 | 14 | // initial state of the button 15 | this.loading = false; 16 | } 17 | 18 | /** 19 | * Show the actual Upload Button. 20 | * 21 | * @returns {*} 22 | */ 23 | view() { 24 | return m('div', {className: 'Button hasIcon flagrow-image-upload-button Button--icon'}, [ 25 | this.loading ? LoadingIndicator.component({className: 'Button-icon'}) : icon('picture-o', {className: 'Button-icon'}), 26 | m('span', {className: 'Button-label'}, this.loading ? app.translator.trans('flagrow-image-upload.forum.states.loading') : app.translator.trans('flagrow-image-upload.forum.buttons.attach')), 27 | m('form#flagrow-image-upload-form', [ 28 | m('input', { 29 | type: 'file', 30 | accept: 'image/*', 31 | name: 'flagrow-image-upload-input', 32 | onchange: this.process.bind(this) 33 | }) 34 | ]) 35 | ]); 36 | } 37 | 38 | /** 39 | * Process the upload event. 40 | * 41 | * @param e 42 | */ 43 | process(e) { 44 | // get the file from the input field 45 | const data = new FormData(); 46 | data.append('image', $(e.target)[0].files[0]); 47 | 48 | // set the button in the loading state (and redraw the element!) 49 | this.loading = true; 50 | m.redraw(); 51 | 52 | // send a POST request to the api 53 | app.request({ 54 | method: 'POST', 55 | url: app.forum.attribute('apiUrl') + '/image/upload', 56 | serialize: raw => raw, 57 | data 58 | }).then( 59 | this.success.bind(this), 60 | this.failure.bind(this) 61 | ); 62 | } 63 | 64 | /** 65 | * Handles errors. 66 | * 67 | * @param message 68 | */ 69 | failure(message) { 70 | // todo show popup 71 | } 72 | 73 | /** 74 | * Appends the image's link to the body of the composer. 75 | * 76 | * @param image 77 | */ 78 | success(image) { 79 | 80 | var link = image.data.attributes.url; 81 | 82 | // create a markdown string that holds the image link 83 | var markdownString = '\n![image ' + link + '](' + link + ')\n'; 84 | 85 | // place the Markdown image link in the Composer 86 | this.textAreaObj.insertAtCursor(markdownString); 87 | 88 | // if we are not starting a new discussion, the variable is defined 89 | if (typeof this.textAreaObj.props.preview !== 'undefined') { 90 | // show what we just uploaded 91 | this.textAreaObj.props.preview(); 92 | } 93 | 94 | // reset the button for a new upload 95 | setTimeout(() => { 96 | document.getElementById("flagrow-image-upload-form").reset(); 97 | this.loading = false; 98 | }, 1000); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/Commands/UploadImageHandler.php: -------------------------------------------------------------------------------- 1 | events = $events; 82 | $this->users = $users; 83 | $this->upload = $upload; 84 | $this->posts = $posts; 85 | $this->app = $app; 86 | $this->validator = $validator; 87 | $this->settings = $settings; 88 | } 89 | 90 | /** 91 | * Handles the command execution. 92 | * 93 | * @param UploadImage $command 94 | * @return null|string 95 | * 96 | * @todo check permission 97 | */ 98 | public function handle(UploadImage $command) 99 | { 100 | // check if the user can upload images, otherwise return 101 | $this->assertCan( 102 | $command->actor, 103 | 'flagrow.image.upload' 104 | ); 105 | 106 | $tmpFile = tempnam($this->app->storagePath() . '/tmp', 'image'); 107 | $command->file->moveTo($tmpFile); 108 | 109 | $file = new UploadedFile( 110 | $tmpFile, 111 | $command->file->getClientFilename(), 112 | $command->file->getClientMediaType(), 113 | $command->file->getSize(), 114 | $command->file->getError(), 115 | true 116 | ); 117 | 118 | // validate the file 119 | $this->validator->maxFileSize = $this->settings->get('flagrow.image-upload.maxFileSize', 2048); 120 | $this->validator->assertValid(['image' => $file]); 121 | 122 | // resize if enabled 123 | if ($this->settings->get('flagrow.image-upload.mustResize')) { 124 | $manager = new ImageManager; 125 | $manager->make($tmpFile)->fit( 126 | $this->settings->get('flagrow.image-upload.resizeMaxWidth', 100), 127 | $this->settings->get('flagrow.image-upload.resizeMaxHeight', 100) 128 | )->save(); 129 | } 130 | 131 | $image = (new Image())->forceFill([ 132 | 'user_id' => $command->actor->id, 133 | 'upload_method' => $this->settings->get('flagrow.image-upload.uploadMethod', 'local'), 134 | 'created_at' => Carbon::now(), 135 | 'file_name' => sprintf( 136 | '%d-%s.%s', 137 | $command->actor->id, 138 | Str::quickRandom(), 139 | $file->guessExtension() ?: 'jpg' 140 | ), 141 | 'file_size' => $file->getSize() 142 | ]); 143 | 144 | // fire the Event ImageWillBeSaved, which can be extended and/or modified elsewhere 145 | $this->events->fire( 146 | new ImageWillBeSaved($command->actor, $image, $file) 147 | ); 148 | 149 | $tmpFilesystem = new Filesystem(new Local(pathinfo($tmpFile, PATHINFO_DIRNAME))); 150 | 151 | $meta = $this->upload->uploadContents( 152 | $image->file_name, 153 | $tmpFilesystem->readAndDelete(pathinfo($tmpFile, PATHINFO_BASENAME)) 154 | ); 155 | 156 | if ($meta) { 157 | $image->file_url = array_get($meta, 'url'); 158 | 159 | if ($image->isDirty()) { 160 | $image->save(); 161 | } 162 | 163 | return $image; 164 | } 165 | 166 | return false; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /js/forum/dist/extension.js: -------------------------------------------------------------------------------- 1 | System.register('flagrow/image-upload/components/UploadButton', ['flarum/Component', 'flarum/helpers/icon', 'flarum/components/LoadingIndicator'], function (_export) { 2 | 'use strict'; 3 | 4 | var Component, icon, LoadingIndicator, UploadButton; 5 | return { 6 | setters: [function (_flarumComponent) { 7 | Component = _flarumComponent['default']; 8 | }, function (_flarumHelpersIcon) { 9 | icon = _flarumHelpersIcon['default']; 10 | }, function (_flarumComponentsLoadingIndicator) { 11 | LoadingIndicator = _flarumComponentsLoadingIndicator['default']; 12 | }], 13 | execute: function () { 14 | UploadButton = (function (_Component) { 15 | babelHelpers.inherits(UploadButton, _Component); 16 | 17 | function UploadButton() { 18 | babelHelpers.classCallCheck(this, UploadButton); 19 | babelHelpers.get(Object.getPrototypeOf(UploadButton.prototype), 'constructor', this).apply(this, arguments); 20 | } 21 | 22 | babelHelpers.createClass(UploadButton, [{ 23 | key: 'init', 24 | 25 | /** 26 | * Load the configured remote uploader service. 27 | */ 28 | value: function init() { 29 | // the service type handling uploads 30 | this.textAreaObj = null; 31 | 32 | // initial state of the button 33 | this.loading = false; 34 | } 35 | 36 | /** 37 | * Show the actual Upload Button. 38 | * 39 | * @returns {*} 40 | */ 41 | }, { 42 | key: 'view', 43 | value: function view() { 44 | return m('div', { className: 'Button hasIcon flagrow-image-upload-button Button--icon' }, [this.loading ? LoadingIndicator.component({ className: 'Button-icon' }) : icon('picture-o', { className: 'Button-icon' }), m('span', { className: 'Button-label' }, this.loading ? app.translator.trans('flagrow-image-upload.forum.states.loading') : app.translator.trans('flagrow-image-upload.forum.buttons.attach')), m('form#flagrow-image-upload-form', [m('input', { 45 | type: 'file', 46 | accept: 'image/*', 47 | name: 'flagrow-image-upload-input', 48 | onchange: this.process.bind(this) 49 | })])]); 50 | } 51 | 52 | /** 53 | * Process the upload event. 54 | */ 55 | }, { 56 | key: 'process', 57 | value: function process(e) { 58 | 59 | var data = new FormData(); 60 | data.append('image', $(e.target)[0].files[0]); 61 | 62 | this.loading = true; 63 | m.redraw(); 64 | 65 | app.request({ 66 | method: 'POST', 67 | url: app.forum.attribute('apiUrl') + '/image/upload', 68 | serialize: function serialize(raw) { 69 | return raw; 70 | }, 71 | data: data 72 | }).then(this.success.bind(this), this.failure.bind(this)); 73 | } 74 | 75 | /** 76 | * Handles errors. 77 | * 78 | * @param message 79 | */ 80 | }, { 81 | key: 'failure', 82 | value: function failure(message) {} 83 | // todo show popup 84 | 85 | /** 86 | * Appends the link to the body of the composer. 87 | * 88 | * @param link 89 | */ 90 | 91 | }, { 92 | key: 'success', 93 | value: function success(image) { 94 | var _this = this; 95 | 96 | var link = image.data.attributes.url; 97 | 98 | // create a markdown string that holds the image link 99 | var markdownString = '\n![image ' + link + '](' + link + ')\n'; 100 | 101 | // place the Markdown image link in the Composer 102 | this.textAreaObj.insertAtCursor(markdownString); 103 | 104 | // if we are not starting a new discussion, the variable is defined 105 | if (typeof this.textAreaObj.props.preview !== 'undefined') { 106 | // show what we just uploaded 107 | this.textAreaObj.props.preview(); 108 | } 109 | 110 | // reset the button for a new upload 111 | setTimeout(function () { 112 | document.getElementById("flagrow-image-upload-form").reset(); 113 | _this.loading = false; 114 | }, 1000); 115 | } 116 | }]); 117 | return UploadButton; 118 | })(Component); 119 | 120 | _export('default', UploadButton); 121 | } 122 | }; 123 | });; 124 | System.register('flagrow/image-upload/main', ['flarum/extend', 'flarum/components/TextEditor', 'flagrow/image-upload/components/UploadButton'], function (_export) { 125 | 'use strict'; 126 | 127 | var extend, TextEditor, UploadButton; 128 | return { 129 | setters: [function (_flarumExtend) { 130 | extend = _flarumExtend.extend; 131 | }, function (_flarumComponentsTextEditor) { 132 | TextEditor = _flarumComponentsTextEditor['default']; 133 | }, function (_flagrowImageUploadComponentsUploadButton) { 134 | UploadButton = _flagrowImageUploadComponentsUploadButton['default']; 135 | }], 136 | execute: function () { 137 | 138 | app.initializers.add('flagrow-image-upload', function (app) { 139 | 140 | /** 141 | * Add the upload button to the post composer. 142 | */ 143 | extend(TextEditor.prototype, 'controlItems', function (items) { 144 | // check whether the user can upload images. If not, returns. 145 | if (!app.forum.attribute('canUploadImages')) return; 146 | 147 | // create and add the button 148 | var uploadButton = new UploadButton(); 149 | uploadButton.textAreaObj = this; 150 | items.add('flagrow-image-upload', uploadButton, 0); 151 | 152 | // animate the button on hover: shows the label 153 | $(".Button-label", ".item-flagrow-image-upload > div").hide(); 154 | $(".item-flagrow-image-upload > div").hover(function () { 155 | $('.Button-label', this).show();$(this).removeClass('Button--icon'); 156 | }, function () { 157 | $('.Button-label', this).hide();$(this).addClass('Button--icon'); 158 | }); 159 | }); 160 | }); 161 | } 162 | }; 163 | }); -------------------------------------------------------------------------------- /js/admin/src/components/ImageUploadPage.js: -------------------------------------------------------------------------------- 1 | import Component from "flarum/Component"; 2 | import Button from "flarum/components/Button"; 3 | import saveSettings from "flarum/utils/saveSettings"; 4 | import Alert from "flarum/components/Alert"; 5 | import FieldSet from "flarum/components/FieldSet"; 6 | import Select from "flarum/components/Select"; 7 | import Switch from "flarum/components/Switch"; 8 | 9 | export default class ImageUploadPage extends Component { 10 | 11 | init() { 12 | // whether we are saving the settings or not right now 13 | this.loading = false; 14 | 15 | // the fields we need to watch and to save 16 | this.fields = [ 17 | 'availableUploadMethods', 18 | 'uploadMethod', 19 | 'imgurClientId', 20 | 'resizeMaxWidth', 21 | 'resizeMaxHeight', 22 | 'cdnUrl', 23 | 'maxFileSize', 24 | 'cloudinaryApiKey', 25 | 'cloudinaryApiSecret', 26 | 'cloudinaryCloudName' 27 | ]; 28 | 29 | // the checkboxes we need to watch and to save. 30 | this.checkboxes = [ 31 | 'mustResize' 32 | ]; 33 | 34 | // options for the dropdown menu 35 | this.uploadMethodOptions = {}; 36 | 37 | this.values = {}; 38 | 39 | // our package prefix (to be added to every field and checkbox in the setting table) 40 | this.settingsPrefix = 'flagrow.image-upload'; 41 | 42 | // get the saved settings from the database 43 | const settings = app.data.settings; 44 | 45 | // set the upload methods 46 | this.uploadMethodOptions = settings[this.addPrefix('availableUploadMethods')]; 47 | // bind the values of the fields and checkboxes to the getter/setter functions 48 | this.fields.forEach(key => this.values[key] = m.prop(settings[this.addPrefix(key)])); 49 | this.checkboxes.forEach(key => this.values[key] = m.prop(settings[this.addPrefix(key)] === '1')); 50 | } 51 | 52 | /** 53 | * Show the actual ImageUploadPage. 54 | * 55 | * @returns {*} 56 | */ 57 | view() { 58 | return [ 59 | m('div', {className: 'ImageUploadPage'}, [ 60 | m('div', {className: 'container'}, [ 61 | m('form', {onsubmit: this.onsubmit.bind(this)}, [ 62 | FieldSet.component({ 63 | label: app.translator.trans('flagrow-image-upload.admin.labels.upload_method'), 64 | children: [ 65 | m('div', {className: 'helpText'}, app.translator.trans('flagrow-image-upload.admin.help_texts.upload_method')), 66 | Select.component({ 67 | options: this.uploadMethodOptions, 68 | onchange: this.values.uploadMethod, 69 | value: this.values.uploadMethod() || 'local' 70 | }) 71 | ] 72 | }), 73 | m('div', {className: 'ImageUploadPage-preferences'}, [ 74 | FieldSet.component({ 75 | label: app.translator.trans('flagrow-image-upload.admin.labels.preferences.title'), 76 | children: [ 77 | m('label', {}, app.translator.trans('flagrow-image-upload.admin.labels.preferences.max_file_size')), 78 | m('input', { 79 | className: 'FormControl', 80 | value: this.values.maxFileSize() || 2048, 81 | oninput: m.withAttr('value', this.values.maxFileSize) 82 | }), 83 | ] 84 | }) 85 | ]), 86 | m('div', {className: 'ImageUploadPage-resize'}, [ 87 | FieldSet.component({ 88 | label: app.translator.trans('flagrow-image-upload.admin.labels.resize.title'), 89 | children: [ 90 | m('div', {className: 'helpText'}, app.translator.trans('flagrow-image-upload.admin.help_texts.resize')), 91 | Switch.component({ 92 | state: this.values.mustResize() || false, 93 | children: app.translator.trans('flagrow-image-upload.admin.labels.resize.toggle'), 94 | onchange: this.values.mustResize 95 | }), 96 | m('label', {}, app.translator.trans('flagrow-image-upload.admin.labels.resize.max_width')), 97 | m('input', { 98 | className: 'FormControl', 99 | value: this.values.resizeMaxWidth() || 100, 100 | oninput: m.withAttr('value', this.values.resizeMaxWidth), 101 | disabled: !this.values.mustResize() 102 | }), 103 | m('label', {}, app.translator.trans('flagrow-image-upload.admin.labels.resize.max_height')), 104 | m('input', { 105 | className: 'FormControl', 106 | value: this.values.resizeMaxHeight() || 100, 107 | oninput: m.withAttr('value', this.values.resizeMaxHeight), 108 | disabled: !this.values.mustResize() 109 | }) 110 | ] 111 | }) 112 | ]), 113 | m('div', {className: 'ImageUploadPage-imgur', style: {display: (this.values.uploadMethod() === 'imgur' ? "block" : "none")}}, [ 114 | FieldSet.component({ 115 | label: app.translator.trans('flagrow-image-upload.admin.labels.imgur.title'), 116 | children: [ 117 | m('label', {}, app.translator.trans('flagrow-image-upload.admin.labels.imgur.client_id')), 118 | m('input', { 119 | className: 'FormControl', 120 | value: this.values.imgurClientId() || '', 121 | oninput: m.withAttr('value', this.values.imgurClientId) 122 | }) 123 | ] 124 | }) 125 | ]), 126 | m('div', {className: 'ImageUploadPage-local', style: {display: (this.values.uploadMethod() === 'local' ? "block" : "none")}}, [ 127 | FieldSet.component({ 128 | label: app.translator.trans('flagrow-image-upload.admin.labels.local.title'), 129 | children: [ 130 | m('label', {}, app.translator.trans('flagrow-image-upload.admin.labels.local.cdn_url')), 131 | m('input', { 132 | className: 'FormControl', 133 | value: this.values.cdnUrl() || '', 134 | oninput: m.withAttr('value', this.values.cdnUrl) 135 | }), 136 | ] 137 | }) 138 | ]), 139 | m('div', {className: 'ImageUploadPage-cloudinary', style: {display: (this.values.uploadMethod() === 'cloudinary' ? "block" : "none")}}, [ 140 | FieldSet.component({ 141 | label: app.translator.trans('flagrow-image-upload.admin.labels.cloudinary.title'), 142 | children: [ 143 | m('label', {}, app.translator.trans('flagrow-image-upload.admin.labels.cloudinary.cloud_name')), 144 | m('input', { 145 | className: 'FormControl', 146 | value: this.values.cloudinaryCloudName() || '', 147 | oninput: m.withAttr('value', this.values.cloudinaryCloudName) 148 | }), 149 | 150 | m('label', {}, app.translator.trans('flagrow-image-upload.admin.labels.cloudinary.api_key')), 151 | m('input', { 152 | className: 'FormControl', 153 | value: this.values.cloudinaryApiKey() || '', 154 | oninput: m.withAttr('value', this.values.cloudinaryApiKey) 155 | }), 156 | 157 | m('label', {}, app.translator.trans('flagrow-image-upload.admin.labels.cloudinary.api_secret')), 158 | m('input', { 159 | className: 'FormControl', 160 | value: this.values.cloudinaryApiSecret() || '', 161 | oninput: m.withAttr('value', this.values.cloudinaryApiSecret) 162 | }) 163 | ] 164 | }) 165 | ]), 166 | Button.component({ 167 | type: 'submit', 168 | className: 'Button Button--primary', 169 | children: app.translator.trans('flagrow-image-upload.admin.buttons.save'), 170 | loading: this.loading, 171 | disabled: !this.changed() 172 | }), 173 | ]) 174 | ]) 175 | ]) 176 | ]; 177 | } 178 | 179 | /** 180 | * Checks if the values of the fields and checkboxes are different from 181 | * the ones stored in the database 182 | * 183 | * @returns bool 184 | */ 185 | changed() { 186 | var fieldsCheck = this.fields.some(key => this.values[key]() !== app.data.settings[this.addPrefix(key)]); 187 | var checkboxesCheck = this.checkboxes.some(key => this.values[key]() !== (app.data.settings[this.addPrefix(key)] == '1')); 188 | return fieldsCheck || checkboxesCheck; 189 | } 190 | 191 | /** 192 | * Saves the settings to the database and redraw the page 193 | * 194 | * @param e 195 | */ 196 | onsubmit(e) { 197 | // prevent the usual form submit behaviour 198 | e.preventDefault(); 199 | 200 | // if the page is already saving, do nothing 201 | if (this.loading) return; 202 | 203 | // prevents multiple savings 204 | this.loading = true; 205 | app.alerts.dismiss(this.successAlert); 206 | 207 | const settings = {}; 208 | 209 | // gets all the values from the form 210 | this.fields.forEach(key => settings[this.addPrefix(key)] = this.values[key]()); 211 | this.checkboxes.forEach(key => settings[this.addPrefix(key)] = this.values[key]()); 212 | 213 | // actually saves everything in the database 214 | saveSettings(settings) 215 | .then(() => { 216 | // on succes, show an alert 217 | app.alerts.show(this.successAlert = new Alert({ 218 | type: 'success', 219 | children: app.translator.trans('core.admin.basics.saved_message') 220 | })); 221 | }) 222 | .catch(() => { 223 | }) 224 | .then(() => { 225 | // return to the initial state and redraw the page 226 | this.loading = false; 227 | m.redraw(); 228 | }); 229 | } 230 | 231 | /** 232 | * Adds the prefix `this.settingsPrefix` at the beginning of `key` 233 | * 234 | * @returns string 235 | */ 236 | addPrefix(key) { 237 | return this.settingsPrefix + '.' + key; 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /js/admin/dist/extension.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | System.register('flagrow/image-upload/addImageUploadPane', ['flarum/extend', 'flarum/components/AdminNav', 'flarum/components/AdminLinkButton', 'flagrow/image-upload/components/ImageUploadPage'], function (_export, _context) { 4 | "use strict"; 5 | 6 | var extend, AdminNav, AdminLinkButton, ImageUploadPage; 7 | 8 | _export('default', function () { 9 | // create the route 10 | app.routes['image-upload'] = { path: '/image-upload', component: ImageUploadPage.component() }; 11 | 12 | // bind the route we created to the three dots settings button 13 | app.extensionSettings['flagrow-image-upload'] = function () { 14 | return m.route(app.route('image-upload')); 15 | }; 16 | 17 | extend(AdminNav.prototype, 'items', function (items) { 18 | // add the Image Upload tab to the admin navigation menu 19 | items.add('image-upload', AdminLinkButton.component({ 20 | href: app.route('image-upload'), 21 | icon: 'picture-o', 22 | children: 'Image Upload', 23 | description: app.translator.trans('flagrow-image-upload.admin.help_texts.description') 24 | })); 25 | }); 26 | }); 27 | 28 | return { 29 | setters: [function (_flarumExtend) { 30 | extend = _flarumExtend.extend; 31 | }, function (_flarumComponentsAdminNav) { 32 | AdminNav = _flarumComponentsAdminNav.default; 33 | }, function (_flarumComponentsAdminLinkButton) { 34 | AdminLinkButton = _flarumComponentsAdminLinkButton.default; 35 | }, function (_flagrowImageUploadComponentsImageUploadPage) { 36 | ImageUploadPage = _flagrowImageUploadComponentsImageUploadPage.default; 37 | }], 38 | execute: function () {} 39 | }; 40 | });; 41 | "use strict"; 42 | 43 | System.register("flagrow/image-upload/components/ImageUploadPage", ["flarum/Component", "flarum/components/Button", "flarum/utils/saveSettings", "flarum/components/Alert", "flarum/components/FieldSet", "flarum/components/Select", "flarum/components/Switch"], function (_export, _context) { 44 | "use strict"; 45 | 46 | var Component, Button, saveSettings, Alert, FieldSet, Select, Switch, ImageUploadPage; 47 | return { 48 | setters: [function (_flarumComponent) { 49 | Component = _flarumComponent.default; 50 | }, function (_flarumComponentsButton) { 51 | Button = _flarumComponentsButton.default; 52 | }, function (_flarumUtilsSaveSettings) { 53 | saveSettings = _flarumUtilsSaveSettings.default; 54 | }, function (_flarumComponentsAlert) { 55 | Alert = _flarumComponentsAlert.default; 56 | }, function (_flarumComponentsFieldSet) { 57 | FieldSet = _flarumComponentsFieldSet.default; 58 | }, function (_flarumComponentsSelect) { 59 | Select = _flarumComponentsSelect.default; 60 | }, function (_flarumComponentsSwitch) { 61 | Switch = _flarumComponentsSwitch.default; 62 | }], 63 | execute: function () { 64 | ImageUploadPage = function (_Component) { 65 | babelHelpers.inherits(ImageUploadPage, _Component); 66 | 67 | function ImageUploadPage() { 68 | babelHelpers.classCallCheck(this, ImageUploadPage); 69 | return babelHelpers.possibleConstructorReturn(this, (ImageUploadPage.__proto__ || Object.getPrototypeOf(ImageUploadPage)).apply(this, arguments)); 70 | } 71 | 72 | babelHelpers.createClass(ImageUploadPage, [{ 73 | key: "init", 74 | value: function init() { 75 | var _this2 = this; 76 | 77 | // whether we are saving the settings or not right now 78 | this.loading = false; 79 | 80 | // the fields we need to watch and to save 81 | this.fields = ['availableUploadMethods', 'uploadMethod', 'imgurClientId', 'resizeMaxWidth', 'resizeMaxHeight', 'cdnUrl', 'maxFileSize', 'cloudinaryApiKey', 'cloudinaryApiSecret', 'cloudinaryCloudName']; 82 | 83 | // the checkboxes we need to watch and to save. 84 | this.checkboxes = ['mustResize']; 85 | 86 | // options for the dropdown menu 87 | this.uploadMethodOptions = {}; 88 | 89 | this.values = {}; 90 | 91 | // our package prefix (to be added to every field and checkbox in the setting table) 92 | this.settingsPrefix = 'flagrow.image-upload'; 93 | 94 | // get the saved settings from the database 95 | var settings = app.data.settings; 96 | 97 | // set the upload methods 98 | this.uploadMethodOptions = settings[this.addPrefix('availableUploadMethods')]; 99 | // bind the values of the fields and checkboxes to the getter/setter functions 100 | this.fields.forEach(function (key) { 101 | return _this2.values[key] = m.prop(settings[_this2.addPrefix(key)]); 102 | }); 103 | this.checkboxes.forEach(function (key) { 104 | return _this2.values[key] = m.prop(settings[_this2.addPrefix(key)] === '1'); 105 | }); 106 | } 107 | }, { 108 | key: "view", 109 | value: function view() { 110 | return [m('div', { className: 'ImageUploadPage' }, [m('div', { className: 'container' }, [m('form', { onsubmit: this.onsubmit.bind(this) }, [FieldSet.component({ 111 | label: app.translator.trans('flagrow-image-upload.admin.labels.upload_method'), 112 | children: [m('div', { className: 'helpText' }, app.translator.trans('flagrow-image-upload.admin.help_texts.upload_method')), Select.component({ 113 | options: this.uploadMethodOptions, 114 | onchange: this.values.uploadMethod, 115 | value: this.values.uploadMethod() || 'local' 116 | })] 117 | }), m('div', { className: 'ImageUploadPage-preferences' }, [FieldSet.component({ 118 | label: app.translator.trans('flagrow-image-upload.admin.labels.preferences.title'), 119 | children: [m('label', {}, app.translator.trans('flagrow-image-upload.admin.labels.preferences.max_file_size')), m('input', { 120 | className: 'FormControl', 121 | value: this.values.maxFileSize() || 2048, 122 | oninput: m.withAttr('value', this.values.maxFileSize) 123 | })] 124 | })]), m('div', { className: 'ImageUploadPage-resize' }, [FieldSet.component({ 125 | label: app.translator.trans('flagrow-image-upload.admin.labels.resize.title'), 126 | children: [m('div', { className: 'helpText' }, app.translator.trans('flagrow-image-upload.admin.help_texts.resize')), Switch.component({ 127 | state: this.values.mustResize() || false, 128 | children: app.translator.trans('flagrow-image-upload.admin.labels.resize.toggle'), 129 | onchange: this.values.mustResize 130 | }), m('label', {}, app.translator.trans('flagrow-image-upload.admin.labels.resize.max_width')), m('input', { 131 | className: 'FormControl', 132 | value: this.values.resizeMaxWidth() || 100, 133 | oninput: m.withAttr('value', this.values.resizeMaxWidth), 134 | disabled: !this.values.mustResize() 135 | }), m('label', {}, app.translator.trans('flagrow-image-upload.admin.labels.resize.max_height')), m('input', { 136 | className: 'FormControl', 137 | value: this.values.resizeMaxHeight() || 100, 138 | oninput: m.withAttr('value', this.values.resizeMaxHeight), 139 | disabled: !this.values.mustResize() 140 | })] 141 | })]), m('div', { className: 'ImageUploadPage-imgur', style: { display: this.values.uploadMethod() === 'imgur' ? "block" : "none" } }, [FieldSet.component({ 142 | label: app.translator.trans('flagrow-image-upload.admin.labels.imgur.title'), 143 | children: [m('label', {}, app.translator.trans('flagrow-image-upload.admin.labels.imgur.client_id')), m('input', { 144 | className: 'FormControl', 145 | value: this.values.imgurClientId() || '', 146 | oninput: m.withAttr('value', this.values.imgurClientId) 147 | })] 148 | })]), m('div', { className: 'ImageUploadPage-local', style: { display: this.values.uploadMethod() === 'local' ? "block" : "none" } }, [FieldSet.component({ 149 | label: app.translator.trans('flagrow-image-upload.admin.labels.local.title'), 150 | children: [m('label', {}, app.translator.trans('flagrow-image-upload.admin.labels.local.cdn_url')), m('input', { 151 | className: 'FormControl', 152 | value: this.values.cdnUrl() || '', 153 | oninput: m.withAttr('value', this.values.cdnUrl) 154 | })] 155 | })]), m('div', { className: 'ImageUploadPage-cloudinary', style: { display: this.values.uploadMethod() === 'cloudinary' ? "block" : "none" } }, [FieldSet.component({ 156 | label: app.translator.trans('flagrow-image-upload.admin.labels.cloudinary.title'), 157 | children: [m('label', {}, app.translator.trans('flagrow-image-upload.admin.labels.cloudinary.cloud_name')), m('input', { 158 | className: 'FormControl', 159 | value: this.values.cloudinaryCloudName() || '', 160 | oninput: m.withAttr('value', this.values.cloudinaryCloudName) 161 | }), m('label', {}, app.translator.trans('flagrow-image-upload.admin.labels.cloudinary.api_key')), m('input', { 162 | className: 'FormControl', 163 | value: this.values.cloudinaryApiKey() || '', 164 | oninput: m.withAttr('value', this.values.cloudinaryApiKey) 165 | }), m('label', {}, app.translator.trans('flagrow-image-upload.admin.labels.cloudinary.api_secret')), m('input', { 166 | className: 'FormControl', 167 | value: this.values.cloudinaryApiSecret() || '', 168 | oninput: m.withAttr('value', this.values.cloudinaryApiSecret) 169 | })] 170 | })]), Button.component({ 171 | type: 'submit', 172 | className: 'Button Button--primary', 173 | children: app.translator.trans('flagrow-image-upload.admin.buttons.save'), 174 | loading: this.loading, 175 | disabled: !this.changed() 176 | })])])])]; 177 | } 178 | }, { 179 | key: "changed", 180 | value: function changed() { 181 | var _this3 = this; 182 | 183 | var fieldsCheck = this.fields.some(function (key) { 184 | return _this3.values[key]() !== app.data.settings[_this3.addPrefix(key)]; 185 | }); 186 | var checkboxesCheck = this.checkboxes.some(function (key) { 187 | return _this3.values[key]() !== (app.data.settings[_this3.addPrefix(key)] == '1'); 188 | }); 189 | return fieldsCheck || checkboxesCheck; 190 | } 191 | }, { 192 | key: "onsubmit", 193 | value: function onsubmit(e) { 194 | var _this4 = this; 195 | 196 | // prevent the usual form submit behaviour 197 | e.preventDefault(); 198 | 199 | // if the page is already saving, do nothing 200 | if (this.loading) return; 201 | 202 | // prevents multiple savings 203 | this.loading = true; 204 | app.alerts.dismiss(this.successAlert); 205 | 206 | var settings = {}; 207 | 208 | // gets all the values from the form 209 | this.fields.forEach(function (key) { 210 | return settings[_this4.addPrefix(key)] = _this4.values[key](); 211 | }); 212 | this.checkboxes.forEach(function (key) { 213 | return settings[_this4.addPrefix(key)] = _this4.values[key](); 214 | }); 215 | 216 | // actually saves everything in the database 217 | saveSettings(settings).then(function () { 218 | // on succes, show an alert 219 | app.alerts.show(_this4.successAlert = new Alert({ 220 | type: 'success', 221 | children: app.translator.trans('core.admin.basics.saved_message') 222 | })); 223 | }).catch(function () { 224 | }).then(function () { 225 | // return to the initial state and redraw the page 226 | _this4.loading = false; 227 | m.redraw(); 228 | }); 229 | } 230 | }, { 231 | key: "addPrefix", 232 | value: function addPrefix(key) { 233 | return this.settingsPrefix + '.' + key; 234 | } 235 | }]); 236 | return ImageUploadPage; 237 | }(Component); 238 | 239 | _export("default", ImageUploadPage); 240 | } 241 | }; 242 | });; 243 | 'use strict'; 244 | 245 | System.register('flagrow/image-upload/main', ['flarum/extend', 'flarum/app', 'flarum/utils/saveSettings', 'flarum/components/PermissionGrid', 'flagrow/image-upload/addImageUploadPane'], function (_export, _context) { 246 | "use strict"; 247 | 248 | var extend, app, saveSettings, PermissionGrid, addImageUploadPane; 249 | return { 250 | setters: [function (_flarumExtend) { 251 | extend = _flarumExtend.extend; 252 | }, function (_flarumApp) { 253 | app = _flarumApp.default; 254 | }, function (_flarumUtilsSaveSettings) { 255 | saveSettings = _flarumUtilsSaveSettings.default; 256 | }, function (_flarumComponentsPermissionGrid) { 257 | PermissionGrid = _flarumComponentsPermissionGrid.default; 258 | }, function (_flagrowImageUploadAddImageUploadPane) { 259 | addImageUploadPane = _flagrowImageUploadAddImageUploadPane.default; 260 | }], 261 | execute: function () { 262 | 263 | app.initializers.add('flagrow-image-upload', function (app) { 264 | // add the admin pane 265 | addImageUploadPane(); 266 | 267 | // add the permission option to the relative pane 268 | extend(PermissionGrid.prototype, 'startItems', function (items) { 269 | items.add('uploadImages', { 270 | icon: 'picture-o', 271 | label: app.translator.trans('flagrow-image-upload.admin.permissions.upload_images_label'), 272 | permission: 'flagrow.image.upload' 273 | }); 274 | }); 275 | }); 276 | } 277 | }; 278 | }); --------------------------------------------------------------------------------