chore!(core): drop deprecated OC.Dialogs.fileexists

It is deprecated since 29.0.0.
Instead use `openConflictPicker` from the `@nextcloud/upload` (Vue 2).
Or from `@nextcloud/dialogs` (every other framework like Vue 3 etc).

Signed-off-by: Ferdinand Thiessen <opensource@fthiessen.de>
This commit is contained in:
Ferdinand Thiessen 2026-01-19 17:14:51 +01:00
parent 9c8f80102a
commit 16f4ff98e4
No known key found for this signature in database
GPG key ID: 7E849AE05218500F
3 changed files with 1 additions and 436 deletions

View file

@ -60,14 +60,6 @@
"count": 1
}
},
"core/src/OC/dialogs.js": {
"camelcase": {
"count": 33
},
"no-undef": {
"count": 1
}
},
"core/src/views/Login.vue": {
"vue/multi-word-component-names": {
"count": 1

View file

@ -9,14 +9,12 @@ import IconCopy from '@mdi/svg/svg/folder-multiple-outline.svg?raw'
import { DialogBuilder, FilePickerType, getFilePickerBuilder } from '@nextcloud/dialogs'
import { t } from '@nextcloud/l10n'
import { spawnDialog } from '@nextcloud/vue/functions/dialog'
import $ from 'jquery'
import { basename } from 'path'
import { defineAsyncComponent } from 'vue'
import logger from '../logger.js'
import OC from './index.js'
/**
* this class to ease the usage of jquery dialogs
* this class to ease the usage of dialogs
*/
const Dialogs = {
// dialog button types
@ -410,396 +408,6 @@ const Dialogs = {
}
return buttonList
},
_fileexistsshown: false,
/**
* Displays file exists dialog
*
* @param {object} data upload object
* @param {object} original file with name, size and mtime
* @param {object} replacement file with name, size and mtime
* @param {object} controller with onCancel, onSkip, onReplace and onRename methods
* @return {Promise} jquery promise that resolves after the dialog template was loaded
*
* @deprecated 29.0.0 Use openConflictPicker from the @nextcloud/upload package instead
*/
fileexists: function(data, original, replacement, controller) {
const self = this
const dialogDeferred = new $.Deferred()
const getCroppedPreview = function(file) {
const deferred = new $.Deferred()
// Only process image files.
const type = file.type && file.type.split('/').shift()
if (window.FileReader && type === 'image') {
const reader = new FileReader()
reader.onload = function(e) {
const blob = new Blob([e.target.result])
window.URL = window.URL || window.webkitURL
const originalUrl = window.URL.createObjectURL(blob)
const image = new Image()
image.src = originalUrl
image.onload = function() {
const url = crop(image)
deferred.resolve(url)
}
}
reader.readAsArrayBuffer(file)
} else {
deferred.reject()
}
return deferred
}
/**
* @param img
*/
function crop(img) {
const canvas = document.createElement('canvas')
const targetSize = 96
const width = img.width
const height = img.height
let x
let y
// Calculate the width and height, constraining the proportions
if (width > height) {
y = 0
x = (width - height) / 2
} else {
y = (height - width) / 2
x = 0
}
const size = Math.min(width, height)
// Set canvas size to the cropped area
canvas.width = size
canvas.height = size
const ctx = canvas.getContext('2d')
ctx.drawImage(img, x, y, size, size, 0, 0, size, size)
// Resize the canvas to match the destination (right size uses 96px)
resampleHermite(canvas, size, size, targetSize, targetSize)
return canvas.toDataURL('image/png', 0.7)
}
/**
* Fast image resize/resample using Hermite filter with JavaScript.
*
* @author ViliusL
*
* @param {*} canvas
* @param {number} W
* @param {number} H
* @param {number} W2
* @param {number} H2
*/
function resampleHermite(canvas, W, H, W2, H2) {
W2 = Math.round(W2)
H2 = Math.round(H2)
const img = canvas.getContext('2d').getImageData(0, 0, W, H)
const img2 = canvas.getContext('2d').getImageData(0, 0, W2, H2)
const data = img.data
const data2 = img2.data
const ratio_w = W / W2
const ratio_h = H / H2
const ratio_w_half = Math.ceil(ratio_w / 2)
const ratio_h_half = Math.ceil(ratio_h / 2)
for (let j = 0; j < H2; j++) {
for (let i = 0; i < W2; i++) {
const x2 = (i + j * W2) * 4
let weight = 0
let weights = 0
let weights_alpha = 0
let gx_r = 0
let gx_g = 0
let gx_b = 0
let gx_a = 0
const center_y = (j + 0.5) * ratio_h
for (let yy = Math.floor(j * ratio_h); yy < (j + 1) * ratio_h; yy++) {
const dy = Math.abs(center_y - (yy + 0.5)) / ratio_h_half
const center_x = (i + 0.5) * ratio_w
const w0 = dy * dy // pre-calc part of w
for (let xx = Math.floor(i * ratio_w); xx < (i + 1) * ratio_w; xx++) {
let dx = Math.abs(center_x - (xx + 0.5)) / ratio_w_half
const w = Math.sqrt(w0 + dx * dx)
if (w >= -1 && w <= 1) {
// hermite filter
weight = 2 * w * w * w - 3 * w * w + 1
if (weight > 0) {
dx = 4 * (xx + yy * W)
// alpha
gx_a += weight * data[dx + 3]
weights_alpha += weight
// colors
if (data[dx + 3] < 255) {
weight = weight * data[dx + 3] / 250
}
gx_r += weight * data[dx]
gx_g += weight * data[dx + 1]
gx_b += weight * data[dx + 2]
weights += weight
}
}
}
}
data2[x2] = gx_r / weights
data2[x2 + 1] = gx_g / weights
data2[x2 + 2] = gx_b / weights
data2[x2 + 3] = gx_a / weights_alpha
}
}
canvas.getContext('2d').clearRect(0, 0, Math.max(W, W2), Math.max(H, H2))
canvas.width = W2
canvas.height = H2
canvas.getContext('2d').putImageData(img2, 0, 0)
}
const addConflict = function($conflicts, original, replacement) {
const $conflict = $conflicts.find('.template').clone().removeClass('template').addClass('conflict')
const $originalDiv = $conflict.find('.original')
const $replacementDiv = $conflict.find('.replacement')
$conflict.data('data', data)
$conflict.find('.filename').text(original.name)
$originalDiv.find('.size').text(OC.Util.humanFileSize(original.size))
$originalDiv.find('.mtime').text(OC.Util.formatDate(original.mtime))
// ie sucks
if (replacement.size && replacement.lastModified) {
$replacementDiv.find('.size').text(OC.Util.humanFileSize(replacement.size))
$replacementDiv.find('.mtime').text(OC.Util.formatDate(replacement.lastModified))
}
let path = original.directory + '/' + original.name
const urlSpec = {
file: path,
x: 96,
y: 96,
c: original.etag,
forceIcon: 0,
}
let previewpath = Files.generatePreviewUrl(urlSpec)
// Escaping single quotes
previewpath = previewpath.replace(/'/g, '%27')
$originalDiv.find('.icon').css({ 'background-image': "url('" + previewpath + "')" })
getCroppedPreview(replacement).then(function(path) {
$replacementDiv.find('.icon').css('background-image', 'url(' + path + ')')
}, function() {
path = OC.MimeType.getIconUrl(replacement.type)
$replacementDiv.find('.icon').css('background-image', 'url(' + path + ')')
})
// connect checkboxes with labels
const checkboxId = $conflicts.find('.conflict').length
$originalDiv.find('input:checkbox').attr('id', 'checkbox_original_' + checkboxId)
$replacementDiv.find('input:checkbox').attr('id', 'checkbox_replacement_' + checkboxId)
$conflicts.append($conflict)
// set more recent mtime bold
// ie sucks
if (replacement.lastModified > original.mtime) {
$replacementDiv.find('.mtime').css('font-weight', 'bold')
} else if (replacement.lastModified < original.mtime) {
$originalDiv.find('.mtime').css('font-weight', 'bold')
} else {
// TODO add to same mtime collection?
}
// set bigger size bold
if (replacement.size && replacement.size > original.size) {
$replacementDiv.find('.size').css('font-weight', 'bold')
} else if (replacement.size && replacement.size < original.size) {
$originalDiv.find('.size').css('font-weight', 'bold')
} else {
// TODO add to same size collection?
}
// TODO show skip action for files with same size and mtime in bottom row
// always keep readonly files
if (original.status === 'readonly') {
$originalDiv
.addClass('readonly')
.find('input[type="checkbox"]')
.prop('checked', true)
.prop('disabled', true)
$originalDiv.find('.message')
.text(t('core', 'read-only'))
}
}
const dialogName = 'oc-dialog-fileexists-content'
const dialogId = '#' + dialogName
if (this._fileexistsshown) {
// add conflict
const $conflicts = $(dialogId + ' .conflicts')
addConflict($conflicts, original, replacement)
const count = $(dialogId + ' .conflict').length
const title = n(
'core',
'{count} file conflict',
'{count} file conflicts',
count,
{ count },
)
$(dialogId).parent().children('.oc-dialog-title').text(title)
// recalculate dimensions
$(window).trigger('resize')
dialogDeferred.resolve()
} else {
// create dialog
this._fileexistsshown = true
$.when(this._getFileExistsTemplate()).then(function($tmpl) {
const title = t('core', 'One file conflict')
const $dlg = $tmpl.octemplate({
dialog_name: dialogName,
title,
type: 'fileexists',
allnewfiles: t('core', 'New Files'),
allexistingfiles: t('core', 'Already existing files'),
why: t('core', 'Which files do you want to keep?'),
what: t('core', 'If you select both versions, the copied file will have a number added to its name.'),
})
$('body').append($dlg)
if (original && replacement) {
const $conflicts = $dlg.find('.conflicts')
addConflict($conflicts, original, replacement)
}
const buttonlist = [{
text: t('core', 'Cancel'),
classes: 'cancel',
click: function() {
if (typeof controller.onCancel !== 'undefined') {
controller.onCancel(data)
}
$(dialogId).ocdialog('close')
},
}, {
text: t('core', 'Continue'),
classes: 'continue',
click: function() {
if (typeof controller.onContinue !== 'undefined') {
controller.onContinue($(dialogId + ' .conflict'))
}
$(dialogId).ocdialog('close')
},
}]
$(dialogId).ocdialog({
width: 500,
closeOnEscape: true,
modal: true,
buttons: buttonlist,
closeButton: null,
close: function() {
self._fileexistsshown = false
try {
$(this).ocdialog('destroy').remove()
} catch {
// ignore
}
},
})
$(dialogId).css('height', 'auto')
const $primaryButton = $dlg.closest('.oc-dialog').find('button.continue')
$primaryButton.prop('disabled', true)
/**
*
*/
function updatePrimaryButton() {
const checkedCount = $dlg.find('.conflicts .checkbox:checked').length
$primaryButton.prop('disabled', checkedCount === 0)
}
// add checkbox toggling actions
$(dialogId).find('.allnewfiles').on('click', function() {
const $checkboxes = $(dialogId).find('.conflict .replacement input[type="checkbox"]')
$checkboxes.prop('checked', $(this).prop('checked'))
})
$(dialogId).find('.allexistingfiles').on('click', function() {
const $checkboxes = $(dialogId).find('.conflict .original:not(.readonly) input[type="checkbox"]')
$checkboxes.prop('checked', $(this).prop('checked'))
})
$(dialogId).find('.conflicts').on('click', '.replacement,.original:not(.readonly)', function() {
const $checkbox = $(this).find('input[type="checkbox"]')
$checkbox.prop('checked', !$checkbox.prop('checked'))
})
$(dialogId).find('.conflicts').on('click', '.replacement input[type="checkbox"],.original:not(.readonly) input[type="checkbox"]', function() {
const $checkbox = $(this)
$checkbox.prop('checked', !$checkbox.prop('checked'))
})
// update counters
$(dialogId).on('click', '.replacement,.allnewfiles', function() {
const count = $(dialogId).find('.conflict .replacement input[type="checkbox"]:checked').length
if (count === $(dialogId + ' .conflict').length) {
$(dialogId).find('.allnewfiles').prop('checked', true)
$(dialogId).find('.allnewfiles + .count').text(t('core', '(all selected)'))
} else if (count > 0) {
$(dialogId).find('.allnewfiles').prop('checked', false)
$(dialogId).find('.allnewfiles + .count').text(t('core', '({count} selected)', { count }))
} else {
$(dialogId).find('.allnewfiles').prop('checked', false)
$(dialogId).find('.allnewfiles + .count').text('')
}
updatePrimaryButton()
})
$(dialogId).on('click', '.original,.allexistingfiles', function() {
const count = $(dialogId).find('.conflict .original input[type="checkbox"]:checked').length
if (count === $(dialogId + ' .conflict').length) {
$(dialogId).find('.allexistingfiles').prop('checked', true)
$(dialogId).find('.allexistingfiles + .count').text(t('core', '(all selected)'))
} else if (count > 0) {
$(dialogId).find('.allexistingfiles').prop('checked', false)
$(dialogId).find('.allexistingfiles + .count')
.text(t('core', '({count} selected)', { count }))
} else {
$(dialogId).find('.allexistingfiles').prop('checked', false)
$(dialogId).find('.allexistingfiles + .count').text('')
}
updatePrimaryButton()
})
dialogDeferred.resolve()
})
.fail(function() {
dialogDeferred.reject()
alert(t('core', 'Error loading file exists template'))
})
}
// }
return dialogDeferred.promise()
},
_getFileExistsTemplate: function() {
const defer = $.Deferred()
if (!this.$fileexistsTemplate) {
const self = this
$.get(OC.filePath('core', 'templates/legacy', 'fileexists.html'), function(tmpl) {
self.$fileexistsTemplate = $(tmpl)
defer.resolve(self.$fileexistsTemplate)
})
.fail(function() {
defer.reject()
})
} else {
defer.resolve(this.$fileexistsTemplate)
}
return defer.promise()
},
}
export default Dialogs

View file

@ -1,35 +0,0 @@
<!--
- SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
- SPDX-License-Identifier: AGPL-3.0-only
-->
<div id="{dialog_name}" title="{title}" class="fileexists">
<span class="why">{why}<!-- Which files do you want to keep --></span><br/>
<span class="what">{what}<!-- If you select both versions, the copied file will have a number added to its name. --></span><br/>
<br/>
<table>
<th><input id="checkbox-allnewfiles" class="allnewfiles checkbox" type="checkbox" /><label for="checkbox-allnewfiles">{allnewfiles}<span class="count"></span></label></th>
<th><input id="checkbox-allexistingfiles" class="allexistingfiles checkbox" type="checkbox" /><label for="checkbox-allexistingfiles">{allexistingfiles}<span class="count"></span></label></th>
</table>
<div class="conflicts">
<div class="template">
<div class="filename"></div>
<div class="replacement">
<input type="checkbox" class="checkbox u-left"/>
<label>
<span class="svg icon"></span>
<div class="mtime"></div>
<div class="size"></div>
</label>
</div>
<div class="original">
<input type="checkbox" class="checkbox u-left" />
<label>
<span class="svg icon"></span>
<div class="mtime"></div>
<div class="size"></div>
<div class="message"></div>
</label>
</div>
</div>
</div>
</div>