Skip to content
Snippets Groups Projects
Commit bb9bb596 authored by Michael Lee's avatar Michael Lee
Browse files

Removing drupal/file_browser (1.3.0)

parent 467f1965
No related branches found
No related tags found
No related merge requests found
Showing
with 0 additions and 884 deletions
<?php
/**
* @file
* Defines library requirements and install routines for File Browser.
*/
use Drupal\Component\Serialization\Yaml;
use Drupal\image\Entity\ImageStyle;
use Drupal\views\Entity\View;
use Drupal\Core\File\FileSystemInterface;
/**
* Implements hook_install().
*/
function file_browser_install() {
// Add an icon for File Browser if we aren't installing from configuration.
if (!\Drupal::isConfigSyncing()) {
$data = file_get_contents(dirname(__FILE__) . '/file_browser_icon.png');
/** @var \Drupal\file\FileInterface $file */
$file = file_save_data($data, 'public://file_browser_icon.png', FileSystemInterface::EXISTS_REPLACE);
if ($file) {
// Set file uuid same as default config.
$uuid = Yaml::decode(file_get_contents(dirname(__FILE__) . '/config/install/embed.button.file_browser.yml'))['icon_uuid'];
$file->set('uuid', $uuid);
$file->save();
\Drupal::service('file.usage')->add($file, 'embed', 'embed_button', 'file_browser');
}
}
}
/**
* Implements hook_uninstall().
*/
function file_browser_uninstall() {
Drupal::configFactory()->getEditable('image.style.file_entity_browser_small')->delete();
Drupal::configFactory()->getEditable('image.style.file_entity_browser_thumbnail')->delete();
Drupal::configFactory()->getEditable('embed.button.file_browser')->delete();
}
/**
* Implements hook_requirements().
*/
function file_browser_requirements($phase) {
$requirements = [];
// Optionally use the Libraries module to determine our library paths.
if (\Drupal::moduleHandler()->moduleExists('libraries')) {
$imagesloaded_path = libraries_get_path('imagesloaded') . '/imagesloaded.pkgd.min.js';
$masonry_path = libraries_get_path('masonry') . '/dist/masonry.pkgd.min.js';
}
else {
$imagesloaded_path = DRUPAL_ROOT . '/libraries/imagesloaded/imagesloaded.pkgd.min.js';
$masonry_path = DRUPAL_ROOT . '/libraries/masonry/dist/masonry.pkgd.min.js';
}
if (!file_exists($imagesloaded_path)) {
$requirements['imagesloaded'] = [
'title' => t('imagesLoaded library missing'),
'description' => t(
'File Browser requires the imagesLoaded library. Download the newest release
from https://github.com/desandro/imagesloaded releases and place it in /libraries'
),
'severity' => REQUIREMENT_ERROR,
];
}
if (!file_exists($masonry_path)) {
$requirements['masonry'] = [
'title' => t('Masonry library missing'),
'description' => t(
'File Browser requires the Masonry library. Download the newest release from
https://github.com/desandro/masonry/releases and place it in /libraries'
),
'severity' => REQUIREMENT_ERROR,
];
}
// Prevent installation if the webserver cannot write to "public://" .
$public_path = \Drupal::service('file_system')->realpath('public://');
if ($public_path !== FALSE && !\Drupal::isConfigSyncing() && !is_writable($public_path)) {
$requirements['public_files_not_writable'] = [
'title' => t('Public files directory not writable'),
'description' => t('The webserver needs to be able to write to the public files directory (public://). Please check your server configuration and try again.'),
'severity' => REQUIREMENT_ERROR,
];
}
return $requirements;
}
/**
* Updates entity browser configuration to remove outdated values.
*/
function file_browser_update_8001() {
/** @var \Drupal\Core\Config\ConfigFactoryInterface $config_factory */
$config_factory = \Drupal::service('config.factory');
$list = $config_factory->listAll('entity_browser.browser');
foreach (['entity_browser.browser.browse_files', 'entity_browser.browser.browse_files_modal'] as $name) {
if (in_array($name, $list, TRUE)) {
$entity_browser = $config_factory->getEditable($name);
if ($text = $entity_browser->get('display_configuration.widget_ids')) {
$entity_browser->clear('display_configuration.widget_ids');
}
if ($name === 'entity_browser.browser.browse_files_modal') {
$entity_browser->set('display_configuration.auto_open', FALSE);
}
$entity_browser->save();
}
}
}
/**
* Updates entity browser configuration to update outdated values.
*/
function file_browser_update_8002() {
/** @var \Drupal\Core\Config\ConfigFactoryInterface $config_factory */
$config_factory = \Drupal::service('config.factory');
$list = $config_factory->listAll('entity_browser.browser');
foreach (['entity_browser.browser.browse_files', 'entity_browser.browser.browse_files_modal'] as $name) {
if (in_array($name, $list, TRUE)) {
$entity_browser = $config_factory->getEditable($name);
$entity_browser->set('selection_display_configuration.selection_hidden', FALSE);
if ($entity_browser->get('widgets.a4ad947c-9669-497c-9988-24351955a02f')) {
$entity_browser->set('widgets.a4ad947c-9669-497c-9988-24351955a02f.settings.auto_select', TRUE);
}
$entity_browser->save();
}
}
/** @var \Drupal\image\Entity\ImageStyle $image_style */
if ($image_style = ImageStyle::load('file_entity_browser_small')) {
$effect = $image_style->getEffect('374fe20a-a27c-45f5-b76d-d851cabcf7b0');
$effect->setConfiguration([
'uuid' => '374fe20a-a27c-45f5-b76d-d851cabcf7b0',
'id' => 'image_scale_and_crop',
'weight' => 1,
'data' => [
'width' => 100,
'height' => 100,
],
]);
$image_style->save();
}
}
/**
* Updates the file browser view to only show permanent files.
*/
function file_browser_update_8003() {
/** @var \Drupal\views\Entity\View $view */
if ($view = View::load('file_entity_browser')) {
$display = &$view->getDisplay('default');
$display['display_options']['filters']['status'] = [
'id' => 'status',
'table' => 'file_managed',
'field' => 'status',
'relationship' => 'none',
'group_type' => 'group',
'admin_label' => '',
'operator' => 'in',
'value' => [
1 => '1',
],
'group' => 1,
'exposed' => FALSE,
'expose' => [
'operator_id' => '',
'label' => '',
'description' => '',
'use_operator' => FALSE,
'operator' => '',
'identifier' => '',
'required' => FALSE,
'remember' => FALSE,
'multiple' => FALSE,
'remember_roles' => [
'authenticated' => 'authenticated',
],
'reduce' => FALSE,
],
'is_grouped' => FALSE,
'group_info' => [
'label' => '',
'description' => '',
'identifier' => '',
'optional' => TRUE,
'widget' => 'select',
'multiple' => FALSE,
'remember' => FALSE,
'default_group' => 'All',
'default_group_multiple' => [],
'group_items' => [],
],
'entity_type' => 'file',
'entity_field' => 'status',
'plugin_id' => 'file_status',
];
$view->save();
}
}
/**
* Updates alpha1 users to use the correct View machine name.
*/
function file_browser_update_8004() {
/** @var \Drupal\Core\Config\ConfigFactoryInterface $config_factory */
$config_factory = \Drupal::service('config.factory');
if (empty($config_factory->get('views.view.file_entity_browser')->getRawData()) && !empty($config_factory->get('views.view.files_entity_browser')->getRawData())) {
$config_factory->rename('views.view.files_entity_browser', 'views.view.file_entity_browser');
$view = $config_factory->getEditable('views.view.file_entity_browser');
$view->set('id', 'file_entity_browser');
$view->save();
// Re-run file_browser_update_8003(), which never ran on the old view.
file_browser_update_8003();
$list = $config_factory->listAll('entity_browser.browser');
foreach (['entity_browser.browser.browse_files', 'entity_browser.browser.browse_files_modal'] as $name) {
if (in_array($name, $list, TRUE)) {
$entity_browser = $config_factory->getEditable($name);
if ($entity_browser->get('widgets.a4ad947c-9669-497c-9988-24351955a02f')) {
$entity_browser->set('widgets.a4ad947c-9669-497c-9988-24351955a02f.settings.view', 'file_entity_browser');
}
$entity_browser->save();
}
}
}
}
/**
* Fix widget configuration if it was broken by a previous update.
*/
function file_browser_update_8005() {
/** @var \Drupal\Core\Config\ConfigFactoryInterface $config_factory */
$config_factory = \Drupal::service('config.factory');
$list = $config_factory->listAll('entity_browser.browser');
$default_uuid = 'a4ad947c-9669-497c-9988-24351955a02f';
foreach (['entity_browser.browser.browse_files', 'entity_browser.browser.browse_files_modal'] as $name) {
if (in_array($name, $list, TRUE)) {
$entity_browser = $config_factory->getEditable($name);
$widgets = $entity_browser->get('widgets');
// If the UUID exists but has no plugin ID, it was added accidentally.
if (isset($widgets[$default_uuid]) && !isset($widgets[$default_uuid]['id'])) {
$entity_browser->clear("widgets.$default_uuid");
$entity_browser->save();
}
}
}
}
iframe:
version: VERSION
css:
theme:
css/file_browser.iframe.css: {}
dropzone_css:
version: VERSION
css:
theme:
css/file_browser.dropzone.css: {}
view:
version: VERSION
js:
js/file_browser.view.js: {}
css:
theme:
css/file_browser.view.css: {}
dependencies:
- core/jquery
- core/drupal
- core/jquery.once
- file_browser/imagesloaded
- file_browser/masonry
preview:
version: VERSION
js:
js/file_browser.preview.js: {}
css:
theme:
css/file_browser.preview.css: {}
dependencies:
- core/jquery
- core/drupal
- core/jquery.once
imagesloaded:
remote: https://github.com/desandro/imagesloaded
version: VERSION
license:
name: MIT
url: https://github.com/desandro/imagesloaded/
gpl-compatible: true
js:
/libraries/imagesloaded/imagesloaded.pkgd.min.js: { minified: true }
dependencies:
- core/jquery
masonry:
remote: http://masonry.desandro.com/
version: VERSION
license:
name: MIT
url: https://github.com/desandro/masonry/
gpl-compatible: true
js:
/libraries/masonry/dist/masonry.pkgd.min.js: { minified: true }
dependencies:
- core/jquery
<?php
/**
* @file
* Contains hook implementations for file_browser.
*/
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\Core\Form\FormStateInterface;
/**
* Implements hook_help().
*/
function file_browser_help($route_name, RouteMatchInterface $route_match) {
switch ($route_name) {
// Main module help for the file_browser module.
case 'help.page.file_browser':
$output = '';
$output .= '<h3>' . t('About') . '</h3>';
$output .= '<p>' . t('Provides an entity browser for files.') . '</p>';
return $output;
default:
}
}
/**
* Implements hook_form_alter().
*/
function file_browser_form_alter(&$form, FormStateInterface &$form_state) {
if (isset($form['#form_id'])) {
if ($form['#form_id'] == 'entity_browser_browse_files_form' ||
$form['#form_id'] == 'entity_browser_browse_files_modal_form') {
file_browser_attach_file_browser_to_form($form);
}
}
}
/**
* Attach the file_browser functionality into a form.
*
* @param array $form
* Nested array of form elements that comprise the form.
*/
function file_browser_attach_file_browser_to_form(array &$form) {
// Attach our library.
$form['#attached']['library'][] = 'file_browser/view';
$form['#attached']['library'][] = 'file_browser/dropzone_css';
if (isset($form['selection_display'])) {
$form['selection_display']['#type'] = 'container';
$form['selection_display']['#attributes']['class'] = ['file-browser-actions'];
}
// Add a class for generic styling.
$form['#attributes']['class'][] = 'file-browser-form';
}
/**
* Implements hook_preprocess_details().
*/
function file_browser_preprocess_details(&$variables) {
if (isset($variables['element']['#id']) && $variables['element']['#id'] == 'edit-field-file-browser-reference') {
$variables['#attached']['library'][] = 'file_browser/iframe';
}
}
/**
* Implements hook_library_info_alter().
*/
function file_browser_library_info_alter(&$libraries, $extension) {
// Optionally use the Libraries module to determine our library paths.
if ($extension == 'file_browser' && \Drupal::moduleHandler()->moduleExists('libraries')) {
$imagesloaded_path = libraries_get_path('imagesloaded') . '/imagesloaded.pkgd.min.js';
$masonry_path = libraries_get_path('masonry') . '/dist/masonry.pkgd.min.js';
$libraries['imagesloaded']['js'] = ['/' . $imagesloaded_path => ['minified' => 'true']];
$libraries['masonry']['js'] = ['/' . $masonry_path => ['minified' => 'true']];
}
}
<?php
/**
* @file
* Post update functions for File Browser.
*/
/**
* @addtogroup updates-8.x-1.x
* @{
*/
/**
* Update default config with default uuid.
*/
function file_browser_post_update_default_uuid() {
$configuration = \Drupal::configFactory()->getEditable('embed.button.file_browser');
// Default uuid in the config.
$uuid = 'db2cad05-1e3b-4b35-b163-99d7d036130c';
// Set file uuid in the config.
$configuration->set('icon_uuid', $uuid);
$configuration->save();
// Load the file_browser_icon form the storage.
$files = \Drupal::entityTypeManager()
->getStorage('file')
->loadByProperties(['uri' => 'public://file_browser_icon.png']);
if (!empty($files)) {
$file = reset($files);
// Set file uuid same as default config.
$file->set('uuid', $uuid);
$file->save();
}
}
/**
* @} End of "addtogroup updates-8.x-1.x".
*/
file_browser.preview:
path: '/admin/file-browser-preview/{file}/{image_style}'
defaults:
_title: 'Preview'
_controller: '\Drupal\file_browser\Controller\FileBrowserController::preview'
image_style: ''
requirements:
_entity_access: 'file.view'
file: \d+
# Who knows why this isn't converted normally...
options:
parameters:
file:
type: entity:file
<?php
/**
* @file
* Provide custom views data for rendering file previews.
*/
/**
* Implements hook_views_data_alter().
*/
function file_browser_views_data_alter(&$data) {
$data['file_managed']['file_browser_preview'] = [
'title' => t('File Browser Preview'),
'help' => t('Displays a preview of a file.'),
'field' => [
'id' => 'file_browser_preview',
],
];
}
web/modules/file_browser/file_browser_icon.png

304 B

<svg fill="white" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>
</svg>
\ No newline at end of file
<svg fill="#000000" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M6 2c-1.1 0-1.99.9-1.99 2L4 20c0 1.1.89 2 1.99 2H18c1.1 0 2-.9 2-2V8l-6-6H6zm7 7V3.5L18.5 9H13z"/>
<path d="M0 0h24v24H0z" fill="none"/>
</svg>
\ No newline at end of file
<svg fill="#dedede" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/>
</svg>
\ No newline at end of file
<svg fill="#444" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/>
<path d="M0 0h24v24H0z" fill="none"/>
</svg>
\ No newline at end of file
/**
* @file file_browser.preview.js
*/
(function ($, Drupal) {
"use strict";
/**
* Contains logic for the view widget.
*/
Drupal.behaviors.fileBrowserPreview = {
attach: function (context, settings) {
var $wrapper = $('#file-browser-preview-wrapper').once('file-browser-preview');
if ($wrapper.length) {
$wrapper.find('select').on('change', function () {
Drupal.ajax({
url: settings.file_browser.preview_path + '/' + $(this).val(),
wrapper: 'file-browser-preview-wrapper'
}).execute();
});
}
}
};
}(jQuery, Drupal));
/**
* @file file_browser.view.js
*/
(function ($, Drupal) {
"use strict";
/**
* Renders the file counter based on our internally tracked count.
*/
function renderFileCounter () {
$('.file-browser-file-counter').each(function () {
$(this).remove();
});
var counter = [];
$('.entities-list [data-entity-id]').each(function () {
if (counter[this.dataset.entityId]) {
++counter[this.dataset.entityId];
}
else {
counter[this.dataset.entityId] = 1;
}
});
for (var id in counter) {
var count = counter[id];
if (count > 0) {
var text = Drupal.formatPlural(count, 'Selected one time', 'Selected @count times');
var $counter = $('<div class="file-browser-file-counter"></div>').text(text);
$('[name="entity_browser_select[file:' + id + ']"]').closest('.grid-item').find('.grid-item-info').prepend($counter);
}
}
}
/**
* Adjusts the padding on the body to account for the fixed actions bar.
*/
function adjustBodyPadding () {
setTimeout(function () {
$('body').css('padding-bottom', $('.file-browser-actions').outerHeight() + 'px');
}, 2000);
}
/**
* Initializes Masonry for the view widget.
*/
Drupal.behaviors.fileBrowserMasonry = {
attach: function (context) {
var $item = $('.grid-item', context);
var $view = $item.parent().once('file-browser-init');
if ($view.length) {
$view.prepend('<div class="grid-sizer"></div><div class="gutter-sizer"></div>');
// Indicate that images are loading.
$view.append('<div class="ajax-progress ajax-progress-fullscreen">&nbsp;</div>');
$view.imagesLoaded(function () {
// Save the scroll position.
var scroll = document.body.scrollTop;
// Remove old Masonry object if it exists. This allows modules like
// Views Infinite Scroll to function with File Browser.
if ($view.data('masonry')) {
$view.masonry('destroy');
}
$view.masonry({
columnWidth: '.grid-sizer',
gutter: '.gutter-sizer',
itemSelector: '.grid-item',
percentPosition: true,
isFitWidth: true
});
// Jump to the old scroll position.
document.body.scrollTop = scroll;
// Add a class to reveal the loaded images, which avoids FOUC.
$item.addClass('item-style');
$view.find('.ajax-progress').remove();
});
}
}
};
/**
* Checks the hidden Entity Browser checkbox when an item is clicked.
*
* This behavior provides backwards-compatibility for users not using
* auto-select and multi-step.
*/
Drupal.behaviors.fileBrowserClickProxy = {
attach: function (context, settings) {
if (!settings.entity_browser_widget.auto_select) {
$('.grid-item', context).once('bind-click-event').click(function () {
var input = $(this).find('.views-field-entity-browser-select input');
input.prop('checked', !input.prop('checked'));
if (input.prop('checked')) {
$(this).addClass('checked');
}
else {
$(this).removeClass('checked');
}
});
}
}
};
/**
* Tracks when entities have been added or removed in the multi-step form,
* and displays that information on each grid item.
*/
Drupal.behaviors.fileBrowserEntityCount = {
attach: function (context) {
adjustBodyPadding();
renderFileCounter();
// Indicate when files have been selected.
var $entities = $('.entities-list', context).once('file-browser-add-count');
if ($entities.length) {
$entities.bind('add-entities', function (event, entity_ids) {
adjustBodyPadding();
renderFileCounter();
});
$entities.bind('remove-entities', function (event, entity_ids) {
adjustBodyPadding();
renderFileCounter();
});
}
}
};
}(jQuery, Drupal));
langcode: en
status: true
dependencies:
module:
- file_browser_example
id: file_browser
label: 'File Browser'
revision: 1
description: 'An example Custom Block type that uses File Browser''s Entity Browser and EntityReferenceImageFormatter.'
langcode: en
status: true
dependencies:
config:
- block_content.type.file_browser
- field.field.block_content.file_browser.field_file_browser_reference
module:
- entity_browser
- file_browser_example
id: block_content.file_browser.default
targetEntityType: block_content
bundle: file_browser
mode: default
content:
field_file_browser_reference:
weight: 3
settings:
entity_browser: browse_files
field_widget_display: thumbnail
field_widget_display_settings:
image_style: medium
third_party_settings: { }
type: entity_browser_entity_reference
region: content
info:
type: string_textfield
weight: -5
settings:
size: 60
placeholder: ''
third_party_settings: { }
region: content
hidden: { }
langcode: en
status: true
dependencies:
config:
- field.field.node.file_browser.body
- field.field.node.file_browser.field_file_browser_image
- node.type.file_browser
module:
- file_browser
- file_browser_example
- path
- text
_core:
default_config_hash: B1eHRl1uKMwrKpcK18CORRnGRIH_TJdpO1ivInSqWSs
id: node.file_browser.default
targetEntityType: node
bundle: file_browser
mode: default
content:
body:
weight: 1
settings:
rows: 9
summary_rows: 3
placeholder: ''
third_party_settings: { }
type: text_textarea_with_summary
region: content
created:
type: datetime_timestamp
weight: 4
settings: { }
third_party_settings: { }
region: content
field_file_browser_image:
weight: 2
settings:
entity_browser: browse_files
field_widget_edit: true
field_widget_remove: true
view_mode: default
open: false
third_party_settings: { }
type: entity_browser_file
region: content
langcode:
type: language_select
weight: 5
settings: { }
third_party_settings: { }
region: content
path:
type: path
weight: 8
settings: { }
third_party_settings: { }
region: content
promote:
type: boolean_checkbox
settings:
display_label: true
weight: 6
third_party_settings: { }
region: content
sticky:
type: boolean_checkbox
settings:
display_label: true
weight: 7
third_party_settings: { }
region: content
title:
type: string_textfield
weight: 0
settings:
size: 60
placeholder: ''
third_party_settings: { }
region: content
uid:
type: entity_reference_autocomplete
weight: 3
settings:
match_operator: CONTAINS
size: 60
placeholder: ''
third_party_settings: { }
region: content
hidden: { }
langcode: en
status: true
dependencies:
config:
- block_content.type.file_browser
- field.field.block_content.file_browser.field_file_browser_reference
module:
- file_browser
- file_browser_example
id: block_content.file_browser.default
targetEntityType: block_content
bundle: file_browser
mode: default
content:
field_file_browser_reference:
weight: 0
label: above
settings:
view_mode: default
link: false
third_party_settings: { }
type: entity_reference_entity_view
region: content
hidden: { }
langcode: en
status: true
dependencies:
config:
- field.field.node.file_browser.body
- field.field.node.file_browser.field_file_browser_image
- node.type.file_browser
module:
- image
- file_browser_example
- text
- user
_core:
default_config_hash: '-YwiZ90Nldz62AXVn6bS3ONKVpHFeoK_Ujm8Pq1ZU4w'
id: node.file_browser.default
targetEntityType: node
bundle: file_browser
mode: default
content:
body:
weight: 3
label: above
settings: { }
third_party_settings: { }
type: text_default
region: content
field_file_browser_image:
weight: 4
label: above
settings:
image_style: ''
image_link: ''
third_party_settings: { }
type: image
region: content
links:
weight: 2
settings: { }
third_party_settings: { }
region: content
hidden:
langcode: true
langcode: en
status: true
dependencies:
config:
- block_content.type.file_browser
- field.storage.block_content.field_file_browser_reference
module:
- file_browser_example
id: block_content.file_browser.field_file_browser_reference
field_name: field_file_browser_reference
entity_type: block_content
bundle: file_browser
label: 'File Browser Reference'
description: ''
required: false
translatable: false
default_value: { }
default_value_callback: ''
settings:
handler: 'default:file'
handler_settings:
target_bundles: null
sort:
field: _none
field_type: entity_reference
langcode: en
status: true
dependencies:
config:
- field.storage.node.body
- node.type.file_browser
module:
- file_browser_example
- text
id: node.file_browser.body
field_name: body
entity_type: node
bundle: file_browser
label: Body
description: ''
required: false
translatable: true
default_value: { }
default_value_callback: ''
settings:
display_summary: false
field_type: text_with_summary
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment