User tests: Successful: Unsuccessful:
Pull Request for Issue # .
Added a button to the media manager edit page to save changes as a copy, without overwriting the original file.
The new files will be named like the original file with an added version
All changes have been applied to the original image in a destructive way. The original image could not be restored.
A copy of the original file is saved with changes applied, and the original file is not modified
Added one new method generateNewName
in 'administrator/components/com_media/src/Controller/ApiController.php' and documentation is added
Status | New | ⇒ | Pending |
Category | ⇒ | Administration com_media JavaScript Repository NPM Change |
Title |
|
Labels |
Added:
NPM Resource Changed
?
|
It should be Save & Close
with the dropdown item Save as Copy
to be consistent with other components.
It should be
Save & Close
with the dropdown itemSave as Copy
to be consistent with other components.
I can change that, but then the save2copy option is not visible. I believe some sort of warning should be presented to the user that he is about to permanently overwrite the file if he clicks on save
There is no warning with the other components. It should be obvious that it is save and close per the label of the button.
There is no warning with the other components. It should be obvious that it is save and close per the label of the button.
Ok, I have swapped the order of the buttons
hmm. Looks like I do not get a copy at all here. No console error.
Hm, just retried on a fresh 4.0 installation, and It worked as expected.
Tested again. The problem is with Firefox
Macintosh:
Firefox: does not work
Safari and Chrome: works fine
I just checked on that, and for some reason, it does not call the method putFile, like it does on Chrome. Possibly an issue with the edit-images.js
. I will look into that later.
Possibly an issue on Firefox side, or even some new security feature they have?
Possibly an issue on Firefox side, or even some new security feature they have?
Clicking on save does trigger a PUT request tho. Save&Close and Save2Copy seem to send a GET request for some reason
The issue is with window.location =
${pathName}?option=com_media&path=${fileDirectory};
(line 165) in
build/media_source/com_media/js/edit-images.es6.js
. If this line is removed, it triggers a PUT request and everything works as expected, but then it does not navigate back of course.
Is it possible that it navigates back before the request is sent, and for that reason it sends a GET request (like it does when entering the site)?
who doing "request" (Joomla.UploadFile) and then change window.location? I wonder how it work at all :)
So how about sending an isClose
parameter in the response and do the navigation after the request has completed?
the navigation after the request has completed
yeap, the navigation should be after the request completed
Further tests:
Normally, Save & Copy does NOT reload the manager. See how it behaves for an article. We stay in edit form with the new article displayed in the form.
Therefore I have tested modifying js to
forUpload.isCopy = true;
Joomla.UploadFile.exec(name, JSON.stringify(forUpload), uploadPath, url, type);
Joomla.MediaManager.Edit.Reset(true); // Changed here
break;
Although we have no message telling that the modified image is saved (would be good to have), we stay in the edit form and it now works fine, waiting for a Save (Apply) or Save & Close.
I think this is the correct behavior.
Remains to add a Success message and, if we could, display the name of the file concerned (whether we use a Save or Save & Copy)
What do you think?
as I see, the code (php part) generate a new file name for a copied file,
so the editor need to be reloaded somehow to use that name, if I am correct
This what Joomla.MediaManager.Edit.Reset(true); does, if I do not mistake.
EDIT: should do in fact. What happens is that the page url remains stuck to the original file.
I also tested
case 'save2copy':
forUpload.isCopy = true;
Joomla.UploadFile.exec(name, JSON.stringify(forUpload), uploadPath, url, type);
Joomla.MediaManager.Edit.Reset(true);
window.location = "".concat(pathName, "?option=com_media&path=").concat(fileDirectory);
break;
and it creates the copy in Firefox, then returns to media manager directly.
and it creates the copy in Firefox, then returns to media manager directly.
But navigating back right after the request was sent means it will navigate even if the request was unsuccessfull for some reason, and since there is no notification yet, the user wouldn't even notice something went wrong. I think the navigation part should be done once the request completes.
I agree. This is why I prefer the first solution, (#30313 (comment)) i.e. display a success message forcing to Save/Save & Close. + an error if fails.
Also display the name or path of file (easy for the existing file before save & Copy, but not so easy for the copied file)
We stay in edit form with the new article displayed in the form.
So does this mean if you click save2copy, you will now be editing the new copy instead of the original file?
I have added the file name in the header and a success message as shown in the screenshot below.
If you click save2copy, the new copy will now be opened. The redirect on save&close will only be performed, if the request has successfully completed. There is no error message yet, because I don't really know how to access the exact error message from xhr.onerror
. If anyone can help, please let me know.
So does this mean if you click save2copy, you will now be editing the new copy instead of the original file?
Yes, that is what I meant. It did not show on screen obviously as we do not display the name of the file we edit.
Will now test your modified PR
@wilsonge
Drone is failing here. https://ci.joomla.org/joomla/joomla-cms/34654/1/25
Could you have a look? Specially for the js errors which look weird. (The rest is common to all PR)
Category | Administration com_media JavaScript Repository NPM Change | ⇒ | Administration com_media Language & Strings JavaScript Repository NPM Change |
@infograf768 I have added support for LTR for the toolbar title. Can you check if this is the correct way to do it?
Labels |
Added:
?
|
I have added support for LTR for the toolbar title. Can you check if this is the correct way to do it?
Nothing to do with LTR. You don't need this here as the placement of the image name is independent from the text.
It was working perfectly as RTL or LTR with the -
in the middle whatever the language.
In RTL, I had already tested it here by adding a fake value in Persian language . The title is RTL while the name of the file remains LTR as should be.
COM_MEDIA_EDIT="یر شناو"
In fact, I suggest you modify the string.
I.e. do as we do when we edit an article.
COM_CONTENT_PAGE_EDIT_ARTICLE="Articles: Edit"
In our case, we would have
COM_MEDIA_EDIT="Media: Edit"
and the code
ToolbarHelper::title(Text::_('COM_MEDIA_EDIT') . ' - ' . $name, 'images mediamanager');
as before
I have tested this item
ok here. failing drone unrelated.
Image is not created when in a directory. Here is the error in console: 409 Conflict
Firefox 79.0 (64-bit) on Windows
I have tested this item
Image is not created when in a directory. Here is the error in console:
409 Conflict
Firefox 79.0 (64-bit) on Windows
This has now been fixed. Also an error message has been added if it failes to save the image
Also we may have an issue when the plugin defines other folders than
images
Sorry I'm not really familiar fith the file system here. Do you mean because it's only checking for existence in the 'images' folder?
while (is_file(Path::check(JPATH_ROOT . '/images/' . dirname($path) . '/' . $name)))
Do you mean because it's only checking for existence in the 'images' folder?
yep
in fact it looks like it could be an issue for generate new name
I confirm we have a 409 conflict
and no error
link is http://localhost:8888/newfolder/joomla40/administrator/index.php?option=com_media&format=json&task=api.files&path=local-1:/my%20sub%20custom/mysubsusb/foldersubsubsub/evenement-sans-titre-01.jpg
params in the plugin (in my case) is
{"directories":{"__field10":{"directory":"images"},"__field11":{"directory":"mycustomimagefolder"}}}
How about utilizing public function search(string $path, string $needle, bool $recursive = false): array;
from adapter to get all files that contain the original file name from any location, and then iterate over these files to generate a new name?
Not sure if there is a better way, but this would probably give me all files containing the file name without verion number from any location, then I could iterate them and generate a new file name:
`
$extension = File::getExt($name);
$name = File::stripExt($name);
$base = count(explode('-', $name)) > 1 ? substr($name, 0, strrpos( $name, '-')) : $name;
$options = [];
$options['search'] = $base;
$options['recursive'] = true;
$files = $model->getFiles($adapter, dirname($path), $options);`
But not too sure how it behaves if you have a file name like 'image-name.jpg' for example, because it strips off everything after the last '-'. I mean it should still get the right images, but could possibly get heaps of unwanted images to iterate over as well.
@infograf768 @Quy please test latest commit, should work now with all folders and subfolders. Also error messages for exceptions have been fixed.
Will test tomorrow. Looks like drone may have an issue with test
I have tested this item
Restarted drone to see if we need new tests or not
I have tested this item
Status | Pending | ⇒ | Ready to Commit |
RTC
Labels |
Added:
?
|
restarted drone again
The system tests fail in the media manager when renaming a folder. That could be related to this PR, but I'm not sure with that.
The screenshot of the test environment when it fails shows an empty folder in the media manager, so maybe it is something wrong with the test?
I have tested this item
When having this PR applied and run npm ci
and cleared browser cache, and then rename an image in the media manager, and immediately edit the same image, I get an internal server error without clear error message. If I then try again to edit the image, it works. So the error happens after rename with first attempt to edit.
Without having this PR applied, that doesn't happen, editing works immediately after renaming.
I think that's also the reason for the failing system test.
@vlaucht Sorry for that, as you have put much time into your PR, and the save2copy works well. I will see if I can find out more details. Please stay tunded, I'm sure we get your PR fixed soon.
Status | Ready to Commit | ⇒ | Pending |
Back to pending due to negative test.
An error has occurred.
0
Call stack
# Function Location
1 () JROOT/plugins/filesystem/local/src/Adapter/LocalAdapter.php:104
2 Joomla\Plugin\Filesystem\Local\Adapter\LocalAdapter->getFile() JROOT/administrator/components/com_media/src/Model/ApiModel.php:93
3 Joomla\Component\Media\Administrator\Model\ApiModel->getFile() JROOT/administrator/components/com_media/src/Model/FileModel.php:65
4 Joomla\Component\Media\Administrator\Model\FileModel->getFileInformation() JROOT/administrator/components/com_media/src/View/File/HtmlView.php:47
5 Joomla\Component\Media\Administrator\View\File\HtmlView->display() JROOT/libraries/src/MVC/Controller/BaseController.php:691
6 Joomla\CMS\MVC\Controller\BaseController->display() JROOT/libraries/src/MVC/Controller/BaseController.php:729
7 Joomla\CMS\MVC\Controller\BaseController->execute() JROOT/libraries/src/Dispatcher/ComponentDispatcher.php:146
8 Joomla\CMS\Dispatcher\ComponentDispatcher->dispatch() JROOT/libraries/src/Component/ComponentHelper.php:389
9 Joomla\CMS\Component\ComponentHelper::renderComponent() JROOT/libraries/src/Application/AdministratorApplication.php:142
10 Joomla\CMS\Application\AdministratorApplication->dispatch() JROOT/libraries/src/Application/AdministratorApplication.php:185
11 Joomla\CMS\Application\AdministratorApplication->doExecute() JROOT/libraries/src/Application/CMSApplication.php:231
12 Joomla\CMS\Application\CMSApplication->execute() JROOT/administrator/includes/app.php:63
13 require_once() JROOT/administrator/index.php:36
and immediately edit the same image, I get an internal server error without clear error message.
Good find. I had not tested that indeed.
@richard67 I will have a look. Also, since it is checking for existence of the file when generating a new name, if you rename lets say file powered_by-4.png
and there is a powered_by-5.png
, and then save2copy powered_by-3.png
I guess it will be saved as powered_by-4.png
instead of powered_by-6.png
since this is not existing anymore. What should be the correct behavior here?
What should be the correct behavior here?
I have no issue getting as new file with an intermediary number as the name will be displayed on top and the file has to be saved anyway. Users should anyway know what they do...
found the issue with editing after renaming, will push a fix soon
So the issue is, that the putFiles method in ApiController returns the file now in
$resp->file = $this->getModel()->getFile($adapter, $path);
return $resp;
instead of return $this->getModel()->getFile($adapter, $path);
.
The renaming is handled in administrator/components/com_media/resources/scripts/store/actions.js
but nothing I change in this file seems to be applied.
@vlaucht You have to be on a development environment and run
npm ci
ornpm run bild:js
to get the changes in your js applied (transpiled to es5 and cpoied to the media folder).
I tried with npm run bild:js
but it's not recompiling the mediamanager.min.js, only the edit-images.js
Ah, wait, that's a different kind of script location. No idea what happens with this.
@nikosdion Could you have a look on it? The issue with this PR currently is described in my test result #30313 (comment) and the comments below it.
No panic, let's see if we get some advise. Unfortunately I'm not the js expert ;-)
It seems to work with npm ci
The miracles of J4 ;-)
Renaming a file works now in the system tests according to the log. Now it fails at renaming a folder, but I'm not sure if a failure or just a timeout which sometimes happens. Will restart the tests to see if they fail at the same place next time.
Labels |
Removed:
?
|
All tests have passed now, please test again
I have tested this item
Now everything works, or I just haven't found a way to break it ;-)
@vlaucht Really cool how fast you have found the reason for the last issue. I'd be happy, and I'm sure it's not only me, to see you contributing more, whenever you have time and mood and however it's comfortable for you.
@richard67 I am not sure why you at-mentioned me in particular. I have not been involved with Media Manager or the JavaScript tooling. I am also not the best person to ask about JavaScript, in general.
@nikosdion Oh, sorry, still am learning who knows what ;-)
No problem :)
I have tested this item
I have tested copying an image several times, modified the copies. Renamed and resized copy. All works as described. Very useful!
Once this string is added, please now look at frontend usage.
When editing an article in frontend, one can use the CMS Content to insert an image
Editing as well as Save as Copy is also available there.
Issue 1: the name of the file edited does not display on top of modal.
The Image
title of the modal is defined in the plugin from PLG_IMAGE_BUTTON_IMAGE
Issue2: Save & Close loads the backend manager. This bug is also present when this PR is not used!
Category | Administration com_media JavaScript Repository NPM Change Language & Strings | ⇒ | JavaScript Administration com_media NPM Change Language & Strings Repository Front End Plugins |
@richard67 Thank you :)
Well, this is never going to end, is it
As for the first issue, I came up with a rather hacky approach. Problem here is, that the modal and the title is rendered before any content is being loaded. There is most likely a better solution for this, but I am lacking some knowledge in how these components are loaded to come up with something better.
(I know that the title is not set back if the modal is closed by backdrop or the close button, don't want do dig too deep in case this solution is not accepted)
Will look into the second issue tomorrow.
Concerning the bug (second issue). We have the same wrong behaviour in the modal when using the CMS insert image.
EDIT: file name is now correctly displayed (original as well as copied)
@dgrammatiko
What do you think about the solution by @vlaucht for displaying the title of the edited image in the modal?
#30313 (comment)
@infograf768 it is wrong in many asspects:
Also not sure why they need the file name in the modal title
We need it when editing the image and save and copy to know the new name
We need it when editing the image and save and copy to know the new name
Well the code is nowhere near the ok mark (at least due to XSS)
As per my understanding, the title is rendered in the modal before any content is loaded, and I could not find a way to change it dynamically on a certain event, other than using a js event listener. I'm open for suggestions if you have a better approach @dgrammatiko
As per my understanding, the title is rendered in the modal before any content is loaded
We should watch out, that this PR is not here to fix all issues in com_media, so I'm not sure if this PR should be responsible to change all this titles and fix all this bugs, which are not directly related to "save2copy" I think.
Probably @vlaucht could split it up in two PRs?
@bembelimen
Except for the save & close pre-existing important bug which loads backend without tmpl component in the modal, the rest is directly related to this new Save & Copy feature.
@infograf768 this part of the code then belongs to the modal not the media field eg bootstrap-legacy.js legacy/bootstrap-init.js
BTW There was a reason I named that file legacy
Please try this:
((customElements, Joomla) => {
if (!Joomla) {
throw new Error('Joomla API is not properly initiated');
}
Joomla.selectedFile = {};
const execTransform = (resp, editor, fieldClass) => {
if (resp.success === true) {
if (resp.data[0].url) {
if (/local-/.test(resp.data[0].adapter)) {
const { rootFull } = Joomla.getOptions('system.paths');
// eslint-disable-next-line prefer-destructuring
Joomla.selectedFile.url = resp.data[0].url.split(rootFull)[1];
if (resp.data[0].thumb_path) {
Joomla.selectedFile.thumb = resp.data[0].thumb_path;
} else {
Joomla.selectedFile.thumb = false;
}
} else if (resp.data[0].thumb_path) {
Joomla.selectedFile.thumb = resp.data[0].thumb_path;
}
} else {
Joomla.selectedFile.url = false;
}
const isElement = (o) => (
typeof HTMLElement === 'object' ? o instanceof HTMLElement
: o && typeof o === 'object' && o !== null && o.nodeType === 1 && typeof o.nodeName === 'string'
);
if (Joomla.selectedFile.url) {
if (!isElement(editor) && (typeof editor !== 'object')) {
Joomla.editors.instances[editor].replaceSelection(`<img loading="lazy" src="${Joomla.selectedFile.url}" alt=""/>`);
} else if (!isElement(editor) && (typeof editor === 'object' && editor.id)) {
window.parent.Joomla.editors.instances[editor.id].replaceSelection(`<img loading="lazy" src="${Joomla.selectedFile.url}" alt=""/>`);
} else {
editor.value = Joomla.selectedFile.url;
fieldClass.updatePreview();
}
}
}
};
/**
* Create and dispatch onMediaFileSelected Event
*
* @param {object} data The data for the detail
*
* @returns {void}
*/
Joomla.getImage = (data, editor, fieldClass) => new Promise((resolve, reject) => {
if (!data || (typeof data === 'object' && (!data.path || data.path === ''))) {
Joomla.selectedFile = {};
resolve({
resp: {
success: false,
},
});
return;
}
const apiBaseUrl = `${Joomla.getOptions('system.paths').rootFull}administrator/index.php?option=com_media&format=json`;
Joomla.request({
url: `${apiBaseUrl}&task=api.files&url=true&path=${data.path}&${Joomla.getOptions('csrf.token')}=1&format=json`,
method: 'GET',
perform: true,
headers: { 'Content-Type': 'application/json' },
onSuccess: (response) => {
const resp = JSON.parse(response);
resolve(execTransform(resp, editor, fieldClass));
},
onError: (err) => {
reject(err);
},
});
});
class JoomlaFieldMedia extends HTMLElement {
constructor() {
super();
this.onSelected = this.onSelected.bind(this);
this.show = this.show.bind(this);
this.clearValue = this.clearValue.bind(this);
this.onMediaFileSelected = this.onMediaFileSelected.bind(this);
this.onMediaFileEdit = this.onMediaFileEdit.bind(this);
this.setTitle = this.setTitle.bind(this);
}
static get observedAttributes() {
return ['type', 'base-path', 'root-folder', 'url', 'modal-container', 'modal-width', 'modal-height', 'input', 'button-select', 'button-clear', 'button-save-selected', 'preview', 'preview-width', 'preview-height'];
}
get type() { return this.getAttribute('type'); }
set type(value) { this.setAttribute('type', value); }
get basePath() { return this.getAttribute('base-path'); }
set basePath(value) { this.setAttribute('base-path', value); }
get rootFolder() { return this.getAttribute('root-folder'); }
set rootFolder(value) { this.setAttribute('root-folder', value); }
get url() { return this.getAttribute('url'); }
set url(value) { this.setAttribute('url', value); }
get modalContainer() { return this.getAttribute('modal-container'); }
set modalContainer(value) { this.setAttribute('modal-container', value); }
get input() { return this.getAttribute('input'); }
set input(value) { this.setAttribute('input', value); }
get buttonSelect() { return this.getAttribute('button-select'); }
set buttonSelect(value) { this.setAttribute('button-select', value); }
get buttonClear() { return this.getAttribute('button-clear'); }
set buttonClear(value) { this.setAttribute('button-clear', value); }
get buttonSaveSelected() { return this.getAttribute('button-save-selected'); }
set buttonSaveSelected(value) { this.setAttribute('button-save-selected', value); }
get modalWidth() { return this.getAttribute(parseInt('modal-width', 10)); }
set modalWidth(value) { this.setAttribute('modal-width', value); }
get modalHeight() { return this.getAttribute(parseInt('modal-height', 10)); }
set modalHeight(value) { this.setAttribute('modal-height', value); }
get previewWidth() { return this.getAttribute(parseInt('preview-width', 10)); }
set previewWidth(value) { this.setAttribute('preview-width', value); }
get previewHeight() { return this.getAttribute(parseInt('preview-height', 10)); }
set previewHeight(value) { this.setAttribute('preview-height', value); }
get preview() { return this.getAttribute('preview'); }
set preview(value) { this.setAttribute('preview', value); }
get previewContainer() { return this.getAttribute('preview-container'); }
// attributeChangedCallback(attr, oldValue, newValue) {}
onMediaFileSelected(e) {
Joomla.selectedFile = e.detail;
this.setTitle(e.detail.file.name);
}
onMediaFileEdit(e) {
this.setTitle(e.detail.file.name);
Joomla.selectedFile = e.detail;
}
connectedCallback() {
this.button = this.querySelector(this.buttonSelect);
this.buttonClearEl = this.querySelector(this.buttonClear);
this.show = this.show.bind(this);
this.modalClose = this.modalClose.bind(this);
this.clearValue = this.clearValue.bind(this);
this.setValue = this.setValue.bind(this);
this.updatePreview = this.updatePreview.bind(this);
this.button.addEventListener('click', this.show);
window.document.addEventListener('onMediaFileSelected', this.onMediaFileSelected);
window.document.addEventListener('onMediaFileEdit', this.onMediaFileEdit);
this.addEventListener('onSetTitle', this.setTitle);
if (this.buttonClearEl) {
this.buttonClearEl.addEventListener('click', this.clearValue);
}
this.updatePreview();
}
disconnectedCallback() {
if (this.button) {
this.button.removeEventListener('click', this.show);
}
if (this.buttonClearEl) {
this.buttonClearEl.removeEventListener('click', this.clearValue);
}
window.document.removeEventListener('onMediaFileSelected', this.onMediaFileSelected);
window.document.removeEventListener('onMediaFileEdit', this.onMediaFileEdit);
this.removeEventListener('onSetTitle', this.setTitle);
}
setTitle(titleString) {
const title = this.querySelector('.modal-title');
if (title) {
const titleEl = title.querySelector('h2') /// or whatever H# is the actual title
if (titleEl) {
titleEl.innerText = `${Joomla.JText._('PLG_IMAGE_BUTTON_IMAGE')}-${titleString}`;
}
}
}
onSelected(event) {
// event.target.removeEventListener('click', this.onSelected);
event.preventDefault();
event.stopPropagation();
this.modalClose();
return false;
}
show() {
this.querySelector('[role="dialog"]').open();
Joomla.selectedFile = {};
this.querySelector(this.buttonSaveSelected).addEventListener('click', this.onSelected);
}
modalClose() {
const input = this.querySelector(this.input);
Joomla.getImage(Joomla.selectedFile, input, this)
.then(() => {
Joomla.Modal.getCurrent().close();
Joomla.selectedFile = {};
})
.catch(() => {
Joomla.Modal.getCurrent().close();
Joomla.selectedFile = {};
Joomla.renderMessages({
error: [Joomla.Text._('JLIB_APPLICATION_ERROR_SERVER')],
});
});
}
setValue(value) {
this.querySelector(this.input).value = value;
this.updatePreview();
}
clearValue() {
this.setValue('');
}
updatePreview() {
if (['true', 'static'].indexOf(this.preview) === -1 || this.preview === 'false') {
return;
}
// Reset preview
if (this.preview) {
const input = this.querySelector(this.input);
const { value } = input;
const div = this.querySelector('.field-media-preview');
if (!value) {
div.innerHTML = '<span class="field-media-preview-icon"></span>';
} else {
div.innerHTML = '';
const imgPreview = new Image();
switch (this.type) {
case 'image':
imgPreview.src = /http/.test(value) ? value : Joomla.getOptions('system.paths').rootFull + value;
imgPreview.setAttribute('alt', '');
break;
default:
// imgPreview.src = dummy image path;
break;
}
div.style.width = this.previewWidth;
div.appendChild(imgPreview);
}
}
}
}
customElements.define('joomla-field-media', JoomlaFieldMedia);
})(customElements, Joomla);
I have solved the save &Close bug here by using in edit-images.js
case 'save':
forUpload.isClose = true;
Joomla.UploadFile.exec(name, JSON.stringify(forUpload), uploadPath, url, type);
if (window.self !== window.top) {
window.location = "".concat(pathName, "?option=com_media&path=").concat(fileDirectory, "&tmpl=component");
} else {
window.location = "".concat(pathName, "?option=com_media&path=").concat(fileDirectory);
}
break;
instead of
case 'save':
forUpload.isClose = true;
Joomla.UploadFile.exec(name, JSON.stringify(forUpload), uploadPath, url, type);
break;
Please try this:
well, I have added the event listeners outside the class like the already existing onMediaFileSelected
because they are not getting triggered the way they are implemented in your solution.
I have solved the save &Close bug here by using in edit-images.js
case 'save': forUpload.isClose = true; Joomla.UploadFile.exec(name, JSON.stringify(forUpload), uploadPath, url, type); if (window.self !== window.top) { window.location = "".concat(pathName, "?option=com_media&path=").concat(fileDirectory, "&tmpl=component"); } else { window.location = "".concat(pathName, "?option=com_media&path=").concat(fileDirectory); } break;
instead of
case 'save': forUpload.isClose = true; Joomla.UploadFile.exec(name, JSON.stringify(forUpload), uploadPath, url, type); break;
This will probably reintroduce the issue with firefox, and also cause the window to close even if the save failed. This should probably be moved to the success case of the response object
Currently the PR has a javascript code style error in drone: https://ci.joomla.org/joomla/joomla-cms/34920/1/25 ... but if it will changed anyway then maybe no need to fix it now.
because they are not getting triggered the way they are implemented in your solution.
They will be triggered the same way as if they were plain listeners in the document. The only difference is that they will be added when the field is initialised, which shouldn't be a problem (before the field is initialised the Media Manager is not even called)
I have added the event listeners outside the class like the already existing onMediaFileSelected
That part is just setting the state in an object, no side processing, the title is processing that needs to be done PER MEDIA FIELD, therefore having a global function which will have to search for the appropriate modal title is:
Update, actually the listeners should be added at the opening of the modal and removed when the modal closes:
((customElements, Joomla) => {
if (!Joomla) {
throw new Error('Joomla API is not properly initiated');
}
Joomla.selectedFile = {};
const execTransform = (resp, editor, fieldClass) => {
if (resp.success === true) {
if (resp.data[0].url) {
if (/local-/.test(resp.data[0].adapter)) {
const { rootFull } = Joomla.getOptions('system.paths');
// eslint-disable-next-line prefer-destructuring
Joomla.selectedFile.url = resp.data[0].url.split(rootFull)[1];
if (resp.data[0].thumb_path) {
Joomla.selectedFile.thumb = resp.data[0].thumb_path;
} else {
Joomla.selectedFile.thumb = false;
}
} else if (resp.data[0].thumb_path) {
Joomla.selectedFile.thumb = resp.data[0].thumb_path;
}
} else {
Joomla.selectedFile.url = false;
}
const isElement = (o) => (
typeof HTMLElement === 'object' ? o instanceof HTMLElement
: o && typeof o === 'object' && o !== null && o.nodeType === 1 && typeof o.nodeName === 'string'
);
if (Joomla.selectedFile.url) {
if (!isElement(editor) && (typeof editor !== 'object')) {
Joomla.editors.instances[editor].replaceSelection(`< img loading = "lazy" src = "${Joomla.selectedFile.url}" alt = "" /> `);
} else if (!isElement(editor) && (typeof editor === 'object' && editor.id)) {
window.parent.Joomla.editors.instances[editor.id].replaceSelection(`< img loading = "lazy" src = "${Joomla.selectedFile.url}" alt = "" /> `);
} else {
editor.value = Joomla.selectedFile.url;
fieldClass.updatePreview();
}
}
}
};
/**
* Create and dispatch onMediaFileSelected Event
*
* @param {object} data The data for the detail
*
* @returns {void}
*/
Joomla.getImage = (data, editor, fieldClass) => new Promise((resolve, reject) => {
if (!data || (typeof data === 'object' && (!data.path || data.path === ''))) {
Joomla.selectedFile = {};
resolve({
resp: {
success: false,
},
});
return;
}
const apiBaseUrl = `${ Joomla.getOptions('system.paths').rootFull } administrator / index.php ? option = com_media & format=json`;
Joomla.request({
url: `${ apiBaseUrl }& task=api.files & url=true & path=${ data.path }& ${ Joomla.getOptions('csrf.token') }=1 & format=json`,
method: 'GET',
perform: true,
headers: { 'Content-Type': 'application/json' },
onSuccess: (response) => {
const resp = JSON.parse(response);
resolve(execTransform(resp, editor, fieldClass));
},
onError: (err) => {
reject(err);
},
});
});
class JoomlaFieldMedia extends HTMLElement {
constructor() {
super();
this.onSelected = this.onSelected.bind(this);
this.show = this.show.bind(this);
this.clearValue = this.clearValue.bind(this);
this.onMediaFileSelected = this.onMediaFileSelected.bind(this);
this.onMediaFileEdit = this.onMediaFileEdit.bind(this);
this.setTitle = this.setTitle.bind(this);
}
static get observedAttributes() {
return ['type', 'base-path', 'root-folder', 'url', 'modal-container', 'modal-width', 'modal-height', 'input', 'button-select', 'button-clear', 'button-save-selected', 'preview', 'preview-width', 'preview-height'];
}
get type() { return this.getAttribute('type'); }
set type(value) { this.setAttribute('type', value); }
get basePath() { return this.getAttribute('base-path'); }
set basePath(value) { this.setAttribute('base-path', value); }
get rootFolder() { return this.getAttribute('root-folder'); }
set rootFolder(value) { this.setAttribute('root-folder', value); }
get url() { return this.getAttribute('url'); }
set url(value) { this.setAttribute('url', value); }
get modalContainer() { return this.getAttribute('modal-container'); }
set modalContainer(value) { this.setAttribute('modal-container', value); }
get input() { return this.getAttribute('input'); }
set input(value) { this.setAttribute('input', value); }
get buttonSelect() { return this.getAttribute('button-select'); }
set buttonSelect(value) { this.setAttribute('button-select', value); }
get buttonClear() { return this.getAttribute('button-clear'); }
set buttonClear(value) { this.setAttribute('button-clear', value); }
get buttonSaveSelected() { return this.getAttribute('button-save-selected'); }
set buttonSaveSelected(value) { this.setAttribute('button-save-selected', value); }
get modalWidth() { return this.getAttribute(parseInt('modal-width', 10)); }
set modalWidth(value) { this.setAttribute('modal-width', value); }
get modalHeight() { return this.getAttribute(parseInt('modal-height', 10)); }
set modalHeight(value) { this.setAttribute('modal-height', value); }
get previewWidth() { return this.getAttribute(parseInt('preview-width', 10)); }
set previewWidth(value) { this.setAttribute('preview-width', value); }
get previewHeight() { return this.getAttribute(parseInt('preview-height', 10)); }
set previewHeight(value) { this.setAttribute('preview-height', value); }
get preview() { return this.getAttribute('preview'); }
set preview(value) { this.setAttribute('preview', value); }
get previewContainer() { return this.getAttribute('preview-container'); }
// attributeChangedCallback(attr, oldValue, newValue) {}
onMediaFileSelected(e) {
Joomla.selectedFile = e.detail;
this.setTitle(e.detail.file.name);
}
onMediaFileEdit(e) {
this.setTitle(e.detail.file.name);
Joomla.selectedFile = e.detail;
}
connectedCallback() {
this.button = this.querySelector(this.buttonSelect);
this.buttonClearEl = this.querySelector(this.buttonClear);
this.show = this.show.bind(this);
this.modalClose = this.modalClose.bind(this);
this.clearValue = this.clearValue.bind(this);
this.setValue = this.setValue.bind(this);
this.updatePreview = this.updatePreview.bind(this);
this.button.addEventListener('click', this.show);
this.addEventListener('onSetTitle', this.setTitle);
if (this.buttonClearEl) {
this.buttonClearEl.addEventListener('click', this.clearValue);
}
this.updatePreview();
}
disconnectedCallback() {
if (this.button) {
this.button.removeEventListener('click', this.show);
}
if (this.buttonClearEl) {
this.buttonClearEl.removeEventListener('click', this.clearValue);
}
this.removeEventListener('onSetTitle', this.setTitle);
}
setTitle(titleString) {
const title = this.querySelector('.modal-title');
if (title) {
const titleEl = title.querySelector('h2') /// or whatever H# is the actual title
if (titleEl) {
titleEl.innerText = `${ Joomla.JText._('PLG_IMAGE_BUTTON_IMAGE') } -${ titleString } `;
}
}
}
onSelected(event) {
// event.target.removeEventListener('click', this.onSelected);
event.preventDefault();
event.stopPropagation();
this.modalClose();
return false;
}
show() {
this.querySelector('[role="dialog"]').open();
Joomla.selectedFile = {};
window.document.addEventListener('onMediaFileSelected', this.onMediaFileSelected);
window.document.addEventListener('onMediaFileEdit', this.onMediaFileEdit);
this.querySelector(this.buttonSaveSelected).addEventListener('click', this.onSelected);
}
modalClose() {
const input = this.querySelector(this.input);
window.document.removeEventListener('onMediaFileSelected', this.onMediaFileSelected);
window.document.removeEventListener('onMediaFileEdit', this.onMediaFileEdit);
Joomla.getImage(Joomla.selectedFile, input, this)
.then(() => {
Joomla.Modal.getCurrent().close();
Joomla.selectedFile = {};
})
.catch(() => {
Joomla.Modal.getCurrent().close();
Joomla.selectedFile = {};
Joomla.renderMessages({
error: [Joomla.Text._('JLIB_APPLICATION_ERROR_SERVER')],
});
});
}
setValue(value) {
this.querySelector(this.input).value = value;
this.updatePreview();
}
clearValue() {
this.setValue('');
}
updatePreview() {
if (['true', 'static'].indexOf(this.preview) === -1 || this.preview === 'false') {
return;
}
// Reset preview
if (this.preview) {
const input = this.querySelector(this.input);
const { value } = input;
const div = this.querySelector('.field-media-preview');
if (!value) {
div.innerHTML = '<span class="field-media-preview-icon"></span>';
} else {
div.innerHTML = '';
const imgPreview = new Image();
switch (this.type) {
case 'image':
imgPreview.src = /http/.test(value) ? value : Joomla.getOptions('system.paths').rootFull + value;
imgPreview.setAttribute('alt', '');
break;
default:
// imgPreview.src = dummy image path;
break;
}
div.style.width = this.previewWidth;
div.appendChild(imgPreview);
}
}
}
}
customElements.define('joomla-field-media', JoomlaFieldMedia);
})(customElements, Joomla);
That part is just setting the state in an object, no side processing, the title is processing that needs to be done PER MEDIA FIELD, therefore having a global function which will have to search for the appropriate modal title is:
I see, sorry I'm still fairly new to this ;-)
@richard67 This is due to wrong string concatination and will be solved
Update, actually the listeners should be added at the opening of the modal and removed when the modal closes:
I'm either missing something, or the event listeners are still not getting registered
I'm either missing something, or the event listeners are still not getting registered
How are you testing this?
The expected workflow is to open the modal and THEN fire an event eg:
document.dispatchEvent(new Event("onMediaFileEdit", {"bubbles":true, "cancelable":false}));
I'm either missing something, or the event listeners are still not getting registered
How are you testing this?
The expected workflow is to open the modal and THEN fire an event eg:
document.dispatchEvent(new Event("onMediaFileEdit", {"bubbles":true, "cancelable":false}));
The same way I did it before. The event gets dispatched on click on the edit button of a particular image, thus, the modal has to be loaded at this point. And it's neither working with
window.parent.document.dispatchEvent(new CustomEvent('onMediaFileEdit',{detail: this.item.name,}));
like it did before, nor with document.dispatchEvent(new Event("onMediaFileEdit", {"bubbles":true, "cancelable":false}));
And it's neither working with
When you're saying it's not working you mean the debugger is not stopping in the relative function or the title is not changing?
I left a comment for the title part as right now I have no clue on top of my head what element is wrapping the title, it might not be H2, that's something you can check by looking at the Dom of the modal
And it's neither working with
When you're saying it's not working you mean the debugger is not stopping in the relative function or the title is not changing?
I left a comment for the title part as right now I have no clue on top of my head what element is wrapping the title, it might not be H2, that's something you can check by looking at the Dom of the modal
I've seen that and changed it already, but I'm talking about the event listener is not getting triggered
I have no clue how you're debugging this but
change the setTitle(titleString)
to
setTitle(titleString) {
const title = this.querySelector('.modal-title');
if (title) {
title.textContent = `${ Joomla.JText._('PLG_IMAGE_BUTTON_IMAGE') } - ${ titleString } `;
}
}
Also fire the events like
document.dispatchEvent(new CustomEvent("onMediaFileEdit", {
detail: {
file: {
name: 'works'
}
}
}))
or window.parent.document....
depending where you are
I have no clue how you're debugging this but
Well, I have tried it with Chrome and Firefox now, and it works fine the way it was implemented before, but the debugger doesnt stop in your implementation. Do you have chrome or firefox to check on that?
I know, but it doesnt even stop if I fire it with the console. As far as I can tell the constructor of JoomlaFieldMedia is not called, and therefore the event listener is not registered. I don't really know why. Would you mind skyping with me real quick?
@vlaucht here you go, replace the files from this zip Archive.zip
and you should be good
Retried on a fresh J4.0 installation with these files applied on chrome and firefox with windows, and it's still not working. Could someone else please test and check wether it is an issue at my end?
You have to make the payload the same in all emitted events. When I wrote the comments above #30313 (comment) I expected you to understand that this was an example, the actual payload should be something like:
window.parent.document.dispatchEvent(
new CustomEvent(
'onMediaFileEdit',
{
"bubbles": true,
"cancelable": false,
"detail": {
path: this.item.path,
thumb: this.item.thumb,
fileType: this.item.mime_type ? this.item.mime_type : false,
extension: this.item.extension ? this.item.extension : false,
file: {
name: this.item.name ? this.item.name : ''
}
},
}
)
);
You have to make the payload the same in all emitted events. When I wrote the comments above [#30313 (comment)]
Well, I do understand that, but as I already mentioned, the constructor of JoomlaFieldMedia
is not being called. Therefore all event listeners inside this class are not registered, thus, no matter what the payload is, they will not be cought. This is why I asked if someone else could test the latest commit and check wether this is an issue at my end.
@vlaucht just delete the media folder then run npm install
. Do you get the needed files? (check the media/system/fields/..., etc)
Also do you get any errors? What's you npm version? Are you sure you're in the right directory?
(You could download the installer from the link below in the Github and try that one if your npm has any errors)
@vlaucht the constructor is called only once, after the js was parsed, you have to debug the actual function eg
setTitle(titleString)
@dgrammatiko I'm pretty sure I have tested it correctly.
I can't find the listener attached to the dom in inspector (I do find the listener registered outside the class)
The debugger does not stop in the setTitle(titleString)
(It does stop in the onMediaFileSelected
registered outside)
Nothing I put in the constructor is being executed
Neither firing with console, nor with UI does trigger onMediaFileEdit
The whole thing simply does not work. Again, thats the reason why I would like someone to test the commit and see if this can be reproduced
The whole thing simply does not work.
Well that's probably something on your side (do you have any browser plugins?) because I shared images that this works on all browsers. Also please share your workflow how you're testing this, I've tested it both manually and also trying to edit an image using the first image in the article form. I can't help you if I don't know what you did and what expected to happen...
BTW the whole js code is bloated for no reason, you don't need an event for the title, eg in the edit and image.vue you can check if the view is modal (there's a state variable for that AFAIK, I added it) and set the title directly there no need for all this complex ritual...
Also please share your workflow how you're testing this
Clone this branch into htdocs folder, run composer install
and npm ci
, clear browser chache, set up the joomla site, install test sample data,
navigate to frontend -> single article -> australian parks ->edit -> CMS content
Open debugger, set breakpoints, check with UI and console
Tested with firefox on windows, only extension is xdebug
I think I was pretty clear many comments ago: you need to open the images modal and then check the debugger
I think I was pretty clear many comments ago: you need to open the images modal and then check the debugger
😉
Well thats what I meant with ->CMS content
. Let me clarify then: CMS content -> Image
I thought this is self explainatory, since the image modal is what is being tested here and CMS content is just an option list.
Ok you're missing most of the code replace the media field with:
((customElements, Joomla) => {
if (!Joomla) {
throw new Error('Joomla API is not properly initiated');
}
Joomla.selectedFile = {};
window.document.addEventListener('onMediaFileSelected', (e) => {
Joomla.selectedFile = e.detail;
});
const execTransform = (resp, editor, fieldClass) => {
if (resp.success === true) {
if (resp.data[0].url) {
if (/local-/.test(resp.data[0].adapter)) {
const { rootFull } = Joomla.getOptions('system.paths');
// eslint-disable-next-line prefer-destructuring
Joomla.selectedFile.url = resp.data[0].url.split(rootFull)[1];
if (resp.data[0].thumb_path) {
Joomla.selectedFile.thumb = resp.data[0].thumb_path;
} else {
Joomla.selectedFile.thumb = false;
}
} else if (resp.data[0].thumb_path) {
Joomla.selectedFile.thumb = resp.data[0].thumb_path;
}
} else {
Joomla.selectedFile.url = false;
}
const isElement = (o) => (
typeof HTMLElement === 'object' ? o instanceof HTMLElement
: o && typeof o === 'object' && o !== null && o.nodeType === 1 && typeof o.nodeName === 'string'
);
if (Joomla.selectedFile.url) {
if (!isElement(editor) && (typeof editor !== 'object')) {
Joomla.editors.instances[editor].replaceSelection(`<img loading="lazy" src="${Joomla.selectedFile.url}" alt=""/>`);
} else if (!isElement(editor) && (typeof editor === 'object' && editor.id)) {
window.parent.Joomla.editors.instances[editor.id].replaceSelection(`<img loading="lazy" src="${Joomla.selectedFile.url}" alt=""/>`);
} else {
editor.value = Joomla.selectedFile.url;
fieldClass.updatePreview();
}
}
}
};
/**
* Create and dispatch onMediaFileSelected Event
*
* @param {object} data The data for the detail
*
* @returns {void}
*/
Joomla.getImage = (data, editor, fieldClass) => new Promise((resolve, reject) => {
if (!data || (typeof data === 'object' && (!data.path || data.path === ''))) {
Joomla.selectedFile = {};
resolve({
resp: {
success: false,
},
});
return;
}
const apiBaseUrl = `${Joomla.getOptions('system.paths').rootFull}administrator/index.php?option=com_media&format=json`;
Joomla.request({
url: `${apiBaseUrl}&task=api.files&url=true&path=${data.path}&${Joomla.getOptions('csrf.token')}=1&format=json`,
method: 'GET',
perform: true,
headers: { 'Content-Type': 'application/json' },
onSuccess: (response) => {
const resp = JSON.parse(response);
resolve(execTransform(resp, editor, fieldClass));
},
onError: (err) => {
reject(err);
},
});
});
class JoomlaFieldMedia extends HTMLElement {
constructor() {
super();
this.onSelected = this.onSelected.bind(this);
this.show = this.show.bind(this);
this.clearValue = this.clearValue.bind(this);
this.modalClose = this.modalClose.bind(this);
this.setValue = this.setValue.bind(this);
this.updatePreview = this.updatePreview.bind(this);
this.onMediaFileEdit = this.onMediaFileEdit.bind(this);
this.setTitle = this.setTitle.bind(this);
}
static get observedAttributes() {
return ['type', 'base-path', 'root-folder', 'url', 'modal-container', 'modal-width', 'modal-height', 'input', 'button-select', 'button-clear', 'button-save-selected', 'preview', 'preview-width', 'preview-height'];
}
get type() { return this.getAttribute('type'); }
set type(value) { this.setAttribute('type', value); }
get basePath() { return this.getAttribute('base-path'); }
set basePath(value) { this.setAttribute('base-path', value); }
get rootFolder() { return this.getAttribute('root-folder'); }
set rootFolder(value) { this.setAttribute('root-folder', value); }
get url() { return this.getAttribute('url'); }
set url(value) { this.setAttribute('url', value); }
get modalContainer() { return this.getAttribute('modal-container'); }
set modalContainer(value) { this.setAttribute('modal-container', value); }
get input() { return this.getAttribute('input'); }
set input(value) { this.setAttribute('input', value); }
get buttonSelect() { return this.getAttribute('button-select'); }
set buttonSelect(value) { this.setAttribute('button-select', value); }
get buttonClear() { return this.getAttribute('button-clear'); }
set buttonClear(value) { this.setAttribute('button-clear', value); }
get buttonSaveSelected() { return this.getAttribute('button-save-selected'); }
set buttonSaveSelected(value) { this.setAttribute('button-save-selected', value); }
get modalWidth() { return this.getAttribute(parseInt('modal-width', 10)); }
set modalWidth(value) { this.setAttribute('modal-width', value); }
get modalHeight() { return this.getAttribute(parseInt('modal-height', 10)); }
set modalHeight(value) { this.setAttribute('modal-height', value); }
get previewWidth() { return this.getAttribute(parseInt('preview-width', 10)); }
set previewWidth(value) { this.setAttribute('preview-width', value); }
get previewHeight() { return this.getAttribute(parseInt('preview-height', 10)); }
set previewHeight(value) { this.setAttribute('preview-height', value); }
get preview() { return this.getAttribute('preview'); }
set preview(value) { this.setAttribute('preview', value); }
get previewContainer() { return this.getAttribute('preview-container'); }
// attributeChangedCallback(attr, oldValue, newValue) {}
connectedCallback() {
this.button = this.querySelector(this.buttonSelect);
this.inputElement = this.querySelector(this.input);
this.buttonClearEl = this.querySelector(this.buttonClear);
this.modalElement = this.querySelector('.joomla-modal');
this.buttonSaveSelectedElement = this.querySelector(this.buttonSaveSelected);
this.previewElement = this.querySelector('.field-media-preview');
if (!this.button || !this.inputElement || !this.buttonClearEl || !this.modalElement
|| !this.buttonSaveSelectedElement) {
throw new Error('Misconfiguaration...');
}
if (Joomla.Bootstrap && Joomla.Bootstrap.initModal && typeof Joomla.Bootstrap.initModal === 'function') {
Joomla.Bootstrap.initModal(this.modalElement);
}
this.button.addEventListener('click', this.show);
if (this.buttonClearEl) {
this.buttonClearEl.addEventListener('click', this.clearValue);
}
this.updatePreview();
}
disconnectedCallback() {
if (this.button) {
this.button.removeEventListener('click', this.show);
}
if (this.buttonClearEl) {
this.buttonClearEl.removeEventListener('click', this.clearValue);
}
}
onSelected(event) {
event.preventDefault();
event.stopPropagation();
this.modalClose();
return false;
}
show() {
this.modalElement.open();
Joomla.selectedFile = {};
window.document.addEventListener('onMediaFileEdit', this.onMediaFileEdit);
this.buttonSaveSelectedElement.addEventListener('click', this.onSelected);
}
modalClose() {
window.document.removeEventListener('onMediaFileEdit', this.onMediaFileEdit);
Joomla.getImage(Joomla.selectedFile, this.inputElement, this)
.then(() => {
Joomla.Modal.getCurrent().close();
Joomla.selectedFile = {};
})
.catch(() => {
Joomla.Modal.getCurrent().close();
Joomla.selectedFile = {};
Joomla.renderMessages({
error: [Joomla.Text._('JLIB_APPLICATION_ERROR_SERVER')],
});
});
}
setValue(value) {
this.inputElement.value = value;
this.updatePreview();
}
clearValue() {
this.setValue('');
}
updatePreview() {
if (['true', 'static'].indexOf(this.preview) === -1 || this.preview === 'false' || !this.previewElement) {
return;
}
// Reset preview
if (this.preview) {
const { value } = this.inputElement;
if (!value) {
this.previewElement.innerHTML = '<span class="field-media-preview-icon"></span>';
} else {
this.previewElement.innerHTML = '';
const imgPreview = new Image();
switch (this.type) {
case 'image':
imgPreview.src = /http/.test(value) ? value : Joomla.getOptions('system.paths').rootFull + value;
imgPreview.setAttribute('alt', '');
break;
default:
// imgPreview.src = dummy image path;
break;
}
this.previewElement.style.width = this.previewWidth;
this.previewElement.appendChild(imgPreview);
}
}
}
onMediaFileEdit(e) {
if (e.detail && e.detail.file && e.detail.file.name) {
this.setTitle(e.detail.file.name);
Joomla.selectedFile = e.detail;
}
}
setTitle(titleString) {
const title = this.querySelector('.modal-title');
if (title) {
title.textContent = `${ Joomla.JText._('PLG_IMAGE_BUTTON_IMAGE') } - ${ titleString } `;
}
}
}
customElements.define('joomla-field-media', JoomlaFieldMedia);
})(customElements, Joomla);
and try again setting the debugger inside setTitle(titleString)
and onMediaFileEdit(e)
Ok you're missing most of the code replace the media field with:
I don't see how this would change anything relevant to the problem, that the listeners are not getting registered. It just moves the methods to the end and removes some lines irrelevant to the issue. (Still tested it tho, but issue is still present)
Honestly I have no clue how you try to debug this but it works fine here
We have been at this point before. I know that it works for you, it just doesnt work for me. I tried console.logs already, but as I already mentioned, the listeners are not registered, so this never gets executed. All listeners registered outside the class work like expected. Since I also tried multiple times with fresh joomla installation, there is nothing I can do besides waiting until someone tests this and can either confirm that this is an issue at my end, or that there is something else wrong.
@vlaucht Sorry I can't help here, am not a js guy ... but I did read you use xdebug. Maybe it's an xdebug issue that the event listeners don't register, or at least an issue which happens only when using xdebug? I remember having read something similar in some other issue in past.
xdebug is for PHP, for debugging JS you need to use the browser's debugger. The PHP debugger will NEVER break on any JS breakpoint...
Silly me ;-)
@richard67 You dont need any JS knowledge. If you follow the steps in Comment and then click on the pencil icon of an image in the modal, it should open the edit view and change the name in the modal title. If not, you can open the browser debug tool, under sources navigate to media/system/fields/joomla-field-media.min.js
and pretty-print it. Then find the methods onMediaFileEdit
and setTitle
and set a breakpoint. Then try to open the edit window and see if the debugger stops, or go to the browser console and try to call
document.dispatchEvent( new CustomEvent( 'onMediaFileEdit', { "bubbles": true, "cancelable": false, "detail": { file: { name: 'test' } }, } ) );
I tested this. I click on the save as copy button, and nothing happens. no errors displayed
I have tested this item
Tested, drop down appears but nothing happens when clicked
This pull request has automatically rebased to 4.2-dev.
Nice feature needs a new start, somewhere else.
Status | Pending | ⇒ | Closed |
Closed_Date | 0000-00-00 00:00:00 | ⇒ | 2022-10-21 09:34:58 |
Closed_By | ⇒ | rdeutz | |
Labels |
Added:
Language Change
Ready to take over
?
Removed: ? ? |
Thank you @vlaucht for your PR