mirror of
https://github.com/chylex/Nextcloud-Desktop.git
synced 2026-04-03 09:11:33 +02:00
Compare commits
72 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6e9755121 | ||
|
|
f283c0c59c | ||
|
|
0d83d7cb68 | ||
|
|
2e10cec2cb | ||
|
|
3802131bad | ||
|
|
5467950f62 | ||
|
|
3ba5bb52f3 | ||
|
|
5c011c5c21 | ||
|
|
532e975687 | ||
|
|
7efc3220f4 | ||
|
|
b5444def55 | ||
|
|
c7ba898fe0 | ||
|
|
7a8c8b19c2 | ||
|
|
72c9d207a0 | ||
|
|
1e4727cfec | ||
|
|
8237c5be69 | ||
|
|
63877d5293 | ||
|
|
737353a017 | ||
|
|
528b5e3108 | ||
|
|
5d098140e7 | ||
|
|
aeb4eed7b5 | ||
|
|
c68b112858 | ||
|
|
b4f2c3c369 | ||
|
|
084f522de1 | ||
|
|
5d5e0220b4 | ||
|
|
7b5ce24302 | ||
|
|
19e33d0924 | ||
|
|
f8a4994307 | ||
|
|
04fb952814 | ||
|
|
8d6e9523bb | ||
|
|
b317cc18d0 | ||
|
|
03a5833e29 | ||
|
|
91a9c65173 | ||
|
|
edabed9594 | ||
|
|
838beded20 | ||
|
|
7dcfe9993c | ||
|
|
b73e2fbdab | ||
|
|
d49771b43a | ||
|
|
928cdf4f4f | ||
|
|
8da6fc40a1 | ||
|
|
6e84a8d420 | ||
|
|
29557ea550 | ||
|
|
37a93dca63 | ||
|
|
b632b7e0fa | ||
|
|
fc8e3b0914 | ||
|
|
861d777899 | ||
|
|
ff3e3ce886 | ||
|
|
567f7eb205 | ||
|
|
c3c8ab85ec | ||
|
|
d1c887d754 | ||
|
|
991f4faafb | ||
|
|
371acb296d | ||
|
|
1119bcf539 | ||
|
|
d67018311f | ||
|
|
eaecec418e | ||
|
|
ab27bcacdf | ||
|
|
536a051460 | ||
|
|
259a117db5 | ||
|
|
167939a8c9 | ||
|
|
41ebfb635e | ||
|
|
017b8e9de3 | ||
|
|
6453c57979 | ||
|
|
a01c73be19 | ||
|
|
f63737ecb0 | ||
|
|
dd87799a82 | ||
|
|
da6b515bfc | ||
|
|
f9a03fa288 | ||
|
|
a5c66cf289 | ||
|
|
41b908e293 | ||
|
|
6ffed87b08 | ||
|
|
bd5ea43547 | ||
|
|
8ba27cb0f1 |
91
.drone.yml
Normal file
91
.drone.yml
Normal file
@@ -0,0 +1,91 @@
|
||||
#
|
||||
# We are building GCC with make and Clang with ninja, the combinations are more
|
||||
# or less arbitrarily chosen. We just want to check that both compilers and both
|
||||
# CMake generators work. It's unlikely that a specific generator only breaks
|
||||
# with a specific compiler.
|
||||
#
|
||||
|
||||
workspace:
|
||||
base: /drone
|
||||
path: src/github.com/owncloud/client
|
||||
|
||||
branches:
|
||||
- master
|
||||
- 2.4
|
||||
|
||||
clone:
|
||||
git:
|
||||
image: plugins/git
|
||||
pull: true
|
||||
tags: false
|
||||
|
||||
pipeline:
|
||||
prepare-clang:
|
||||
image: owncloudci/client:latest
|
||||
pull: true
|
||||
environment:
|
||||
- LC_ALL=C.UTF-8
|
||||
commands:
|
||||
- mkdir clang-build
|
||||
- cd clang-build
|
||||
- cmake -GNinja -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE="Debug" -DUNIT_TESTING=1 ..
|
||||
|
||||
building-clang:
|
||||
image: owncloudci/client:latest
|
||||
pull: true
|
||||
environment:
|
||||
- LC_ALL=C.UTF-8
|
||||
commands:
|
||||
- cd clang-build
|
||||
- ninja -j4
|
||||
|
||||
testing-clang:
|
||||
image: owncloudci/client:latest
|
||||
pull: true
|
||||
environment:
|
||||
- LC_ALL=C.UTF-8
|
||||
commands:
|
||||
- cd clang-build
|
||||
- useradd -m -s /bin/bash tester
|
||||
- chown -R tester:tester .
|
||||
- su-exec tester ctest --output-on-failure
|
||||
|
||||
prepare-gcc:
|
||||
image: owncloudci/client:latest
|
||||
pull: true
|
||||
environment:
|
||||
- LC_ALL=C.UTF-8
|
||||
commands:
|
||||
- mkdir gcc-build
|
||||
- cd gcc-build
|
||||
- cmake -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_BUILD_TYPE="Debug" -DUNIT_TESTING=1 ..
|
||||
|
||||
building-gcc:
|
||||
image: owncloudci/client:latest
|
||||
pull: true
|
||||
environment:
|
||||
- LC_ALL=C.UTF-8
|
||||
commands:
|
||||
- cd gcc-build
|
||||
- make -j4
|
||||
|
||||
testing-gcc:
|
||||
image: owncloudci/client:latest
|
||||
pull: true
|
||||
environment:
|
||||
- LC_ALL=C.UTF-8
|
||||
commands:
|
||||
- cd gcc-build
|
||||
- useradd -m -s /bin/bash tester
|
||||
- chown -R tester:tester .
|
||||
- su-exec tester ctest --output-on-failure
|
||||
|
||||
notify-slack:
|
||||
image: plugins/slack
|
||||
pull: true
|
||||
secrets: [ slack_webhook ]
|
||||
channel: desktop
|
||||
when:
|
||||
local: false
|
||||
status: [ changed, failure ]
|
||||
event: [ push ]
|
||||
37
.travis.yml
37
.travis.yml
@@ -1,37 +0,0 @@
|
||||
sudo: required
|
||||
|
||||
language: cpp
|
||||
|
||||
services:
|
||||
- docker
|
||||
|
||||
branches:
|
||||
only:
|
||||
- coverity_scan
|
||||
|
||||
before_install:
|
||||
- sudo sh -c "echo 'deb http://download.opensuse.org/repositories/isv:/ownCloud:/desktop/Ubuntu_14.04/ /' >> /etc/apt/sources.list.d/owncloud-client.list"
|
||||
- sudo sh -c "echo 'deb-src http://download.opensuse.org/repositories/isv:/ownCloud:/desktop/Ubuntu_14.04/ /' >> /etc/apt/sources.list.d/owncloud-client.list"
|
||||
- wget http://download.opensuse.org/repositories/isv:ownCloud:desktop/Ubuntu_14.04/Release.key
|
||||
- sudo apt-key add - < Release.key
|
||||
- sudo apt-get update
|
||||
- sudo apt-get -y build-dep owncloud-client
|
||||
- checkout=$(git show-ref --head --hash head)
|
||||
- cd ../
|
||||
- wget https://scan.coverity.com/download/linux-64 --post-data "token=$token&project=owncloud%2Fmirall" -O coverity_tool.tgz
|
||||
- mkdir coverity
|
||||
- tar -xvf coverity_tool.tgz -C coverity --strip-components=1
|
||||
- export PATH=$PATH:$PWD/coverity/bin/
|
||||
- cd $TRAVIS_BUILD_DIR
|
||||
|
||||
install:
|
||||
- cd ../
|
||||
- mkdir client-build
|
||||
- cd client-build
|
||||
- cmake -DCMAKE_BUILD_TYPE="Debug" $TRAVIS_BUILD_DIR
|
||||
- cov-build --dir cov-int make
|
||||
- tar czvf client.tgz cov-int
|
||||
- curl --form token=$token --form email=lukas@statuscode.ch --form file=@$PWD/client.tgz --form version="$checkout" --form description="$checkout" https://scan.coverity.com/builds?project=owncloud%2Fmirall
|
||||
|
||||
# Hack to stop processing
|
||||
script: true
|
||||
17
ChangeLog
17
ChangeLog
@@ -1,6 +1,23 @@
|
||||
ChangeLog
|
||||
=========
|
||||
|
||||
version 2.4.2 (2018-06-xx)
|
||||
* Linux: Tray workarounds (#6545)
|
||||
* Fix nautilus/nemo shell issues (#6393, #6406)
|
||||
* Updater: Add update channel feature (#6259)
|
||||
* Updater: Support EXE->MSI upgrade
|
||||
* SyncJournal: Fixes for sync folders on removable media (#6049, #6049)
|
||||
* SslButton: Add HTTP/2 info (#3146)
|
||||
* Fix assert when using ownCloud server 5 (which you should not) (#6403)
|
||||
* Normalize local path (#4424)
|
||||
* Blacklisting must prevent parent etag updates (#6411)
|
||||
* macdeployqt: Adjust minimum version based on our Qt (#5932)
|
||||
* macOS: Unload the Finder extension on exit (#5382, #3819)
|
||||
* Upload: Adjust timeout for final job based on file size (#6527)
|
||||
* Sync: When detecting a local move, keep the local mtime (#6629)
|
||||
* Credentials: Retry fetching from the keychain in case the keychain is still starting (#4274, #6522)
|
||||
* OAuth2: Try to refresh the token even if the credentials weren't ready (#6522)
|
||||
|
||||
version 2.4.1 (2018-02-xx)
|
||||
* Ignore files with file names that can't be encoded for the filesystem (#6287, #5676, #5719)
|
||||
* Issues: Speed up insertion and add hard upper limit (#6272)
|
||||
|
||||
51
Jenkinsfile
vendored
51
Jenkinsfile
vendored
@@ -1,51 +0,0 @@
|
||||
#!groovy
|
||||
|
||||
//
|
||||
// We now run the tests in Debug mode so that ASSERTs are triggered.
|
||||
// Ideally we should run the tests in both Debug and Release so we catch
|
||||
// all possible error combinations.
|
||||
// See also the top comment in syncenginetestutils.h
|
||||
//
|
||||
|
||||
node('CLIENT') {
|
||||
stage 'Checkout'
|
||||
checkout scm
|
||||
sh '''git submodule update --init'''
|
||||
|
||||
stage 'Qt5'
|
||||
sh '''rm -rf build
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE="Debug" -DUNIT_TESTING=1 -DWITH_TESTING=1 -DCMAKE_PREFIX_PATH=/var/lib/jenkins/qt/5.6.2 ..
|
||||
make -j4
|
||||
ctest -V --output-on-failure'''
|
||||
|
||||
stage 'Qt5 - clang'
|
||||
sh '''rm -rf build
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DUNIT_TESTING=1 -DWITH_TESTING=1 -DCMAKE_PREFIX_PATH=/var/lib/jenkins/qt/5.6.2 ..
|
||||
make -j4
|
||||
ctest -V --output-on-failure'''
|
||||
|
||||
|
||||
stage 'Win32'
|
||||
def win32 = docker.image('guruz/docker-owncloud-client-win32:latest')
|
||||
win32.pull() // make sure we have the latest available from Docker Hub
|
||||
win32.inside {
|
||||
sh '''
|
||||
rm -rf build-win32
|
||||
mkdir build-win32
|
||||
cd build-win32
|
||||
../admin/win/download_runtimes.sh
|
||||
cmake .. -DCMAKE_TOOLCHAIN_FILE=../admin/win/Toolchain-mingw32-openSUSE.cmake -DWITH_CRASHREPORTER=ON
|
||||
make -j4
|
||||
make package
|
||||
ctest .
|
||||
'''
|
||||
}
|
||||
|
||||
// Stage 'macOS' TODO
|
||||
}
|
||||
|
||||
|
||||
@@ -19,4 +19,4 @@ set( MAC_INSTALLER_BACKGROUND_FILE "${CMAKE_SOURCE_DIR}/admin/osx/installer-back
|
||||
|
||||
option( WITH_CRASHREPORTER "Build crashreporter" OFF )
|
||||
set( CRASHREPORTER_SUBMIT_URL "https://crash-reports.owncloud.com/submit" CACHE string "URL for crash reporter" )
|
||||
set( CRASHREPORTER_ICON ":/owncloud-icon.png" )
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ownCloud Desktop Client
|
||||
|
||||
[](https://jenkins.owncloud.org/job/owncloud-client/job/client/job/master/)
|
||||
[](https://drone.owncloud.com/owncloud/client)
|
||||
|
||||
## Introduction
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
set( MIRALL_VERSION_MAJOR 2 )
|
||||
set( MIRALL_VERSION_MINOR 4 )
|
||||
set( MIRALL_VERSION_PATCH 1 )
|
||||
set( MIRALL_VERSION_PATCH 2 )
|
||||
set( MIRALL_VERSION_YEAR 2018 )
|
||||
set( MIRALL_SOVERSION 0 )
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ else()
|
||||
set(MAC_INSTALLER_DO_CUSTOM_BACKGROUND "0")
|
||||
endif()
|
||||
|
||||
find_package(Qt5 5.6 COMPONENTS Core REQUIRED)
|
||||
configure_file(create_mac_pkg.sh.cmake ${CMAKE_CURRENT_BINARY_DIR}/create_mac.sh)
|
||||
configure_file(macosx.pkgproj ${CMAKE_CURRENT_BINARY_DIR}/macosx.pkgproj)
|
||||
configure_file(pre_install.sh.cmake ${CMAKE_CURRENT_BINARY_DIR}/pre_install.sh)
|
||||
|
||||
@@ -23,7 +23,7 @@ identity="$3"
|
||||
prjfile=$build_path/admin/osx/macosx.pkgproj
|
||||
|
||||
# The name of the installer package
|
||||
installer="@APPLICATION_SHORTNAME@-@MIRALL_VERSION_FULL@@MIRALL_VERSION_SUFFIX@"
|
||||
installer="@APPLICATION_SHORTNAME@-qt@Qt5Core_VERSION@-@MIRALL_VERSION_FULL@@MIRALL_VERSION_SUFFIX@"
|
||||
installer_file="$installer.pkg"
|
||||
installer_file_tar="$installer.pkg.tar"
|
||||
installer_file_tar_bz2="$installer.pkg.tar.bz2"
|
||||
|
||||
@@ -22,6 +22,7 @@ import subprocess
|
||||
import commands
|
||||
import sys
|
||||
from glob import glob
|
||||
from distutils.version import LooseVersion
|
||||
|
||||
def QueryQMake(attrib):
|
||||
return subprocess.check_output([qmake_path, '-query', attrib]).rstrip('\n')
|
||||
@@ -92,6 +93,8 @@ commands.append(['mkdir', '-p', resources_dir])
|
||||
plugins_dir = os.path.join(bundle_dir, 'Contents', 'PlugIns')
|
||||
binaries = [i for i in glob(os.path.join(bundle_dir, 'Contents', 'MacOS', "*")) if is_exe(i)];
|
||||
|
||||
qt_version = QueryQMake('QT_VERSION')
|
||||
print "Using Qt", qt_version
|
||||
|
||||
fixed_libraries = []
|
||||
fixed_frameworks = []
|
||||
@@ -334,9 +337,19 @@ def FindQtPlugin(name):
|
||||
for binary in binaries:
|
||||
FixBinary(binary)
|
||||
|
||||
|
||||
if LooseVersion(qt_version) >= LooseVersion("5.10.0"):
|
||||
QT_PLUGINS.append('styles/libqmacstyle.dylib')
|
||||
for plugin in QT_PLUGINS:
|
||||
FixPlugin(FindQtPlugin(plugin), os.path.dirname(plugin))
|
||||
|
||||
if LooseVersion(qt_version) >= LooseVersion("5.10.0"):
|
||||
args = ['plutil', '-insert', 'LSMinimumSystemVersion', '-string', '10.10.0', os.path.join(bundle_dir, 'Contents', 'Info.plist')]
|
||||
commands.append(args)
|
||||
else:
|
||||
args = ['plutil', '-insert', 'LSMinimumSystemVersion', '-string', '10.7.0', os.path.join(bundle_dir, 'Contents', 'Info.plist')]
|
||||
commands.append(args)
|
||||
|
||||
if len(sys.argv) <= 2:
|
||||
print 'Will run %d commands:' % len(commands)
|
||||
for command in commands:
|
||||
|
||||
@@ -27,11 +27,9 @@
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>@MIRALL_VERSION_STRING@</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>(C) 2014-2016 @APPLICATION_VENDOR@</string>
|
||||
<string>(C) 2014-2018 @APPLICATION_VENDOR@</string>
|
||||
<key>SUShowReleaseNotes</key>
|
||||
<false/>
|
||||
<key>LSMinimumBundleVersion</key>
|
||||
<string>10.7.0</string>
|
||||
<key>SUPublicDSAKeyFile</key>
|
||||
<string>dsa_pub.pem</string>
|
||||
</dict>
|
||||
|
||||
@@ -278,6 +278,90 @@ X-GNOME-Autostart-Delay=3
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
|
||||
|
||||
# Translations
|
||||
Comment[oc]=@APPLICATION_NAME@ sincronizacion del client
|
||||
GenericName[oc]=Dorsièr de Sincronizacion
|
||||
@@ -362,6 +446,10 @@ Comment[ko]=@APPLICATION_NAME@ 데스크톱 동기화 클라이언트
|
||||
GenericName[ko]=폴더 동기화
|
||||
Name[ko]=@APPLICATION_NAME@ 데스크톱 동기화 클라이언트
|
||||
Icon[ko]=@APPLICATION_EXECUTABLE@
|
||||
Comment[lo]=@APPLICATION_NAME@ ການປະສານຂໍ້ມູນຄອມພິວເຕີລູກຂ່າຍ
|
||||
GenericName[lo]=ໂຟນເດີຊິງ
|
||||
Name[lo]=@APPLICATION_NAME@ ຊິງຄອມພິວເຕີລູກຂ່າຍ
|
||||
Icon[lo]=@APPLICATION_EXECUTABLE@
|
||||
Comment[hu_HU]=@APPLICATION_NAME@ asztali szinkronizációs kliens
|
||||
GenericName[hu_HU]=Könyvtár szinkronizálás
|
||||
Name[hu_HU]=@APPLICATION_NAME@ asztali szinkr. kliens
|
||||
|
||||
@@ -34,12 +34,15 @@ from gi.repository import GObject, Nautilus
|
||||
appname = 'ownCloud'
|
||||
|
||||
print("Initializing "+appname+"-client-nautilus extension")
|
||||
print("Using python version {}".format(sys.version_info))
|
||||
|
||||
def get_local_path(url):
|
||||
if url[0:7] == 'file://':
|
||||
url = url[7:]
|
||||
unquote = urllib.parse.unquote if python3 else urllib.unquote
|
||||
return unquote(url)
|
||||
if python3:
|
||||
return urllib.parse.unquote(url)
|
||||
else:
|
||||
return urllib.unquote(url).decode('utf-8')
|
||||
|
||||
def get_runtime_dir():
|
||||
"""Returns the value of $XDG_RUNTIME_DIR, a directory path.
|
||||
@@ -61,7 +64,7 @@ class SocketConnect(GObject.GObject):
|
||||
self._watch_id = 0
|
||||
self._sock = None
|
||||
self._listeners = [self._update_registered_paths]
|
||||
self._remainder = ''.encode()
|
||||
self._remainder = ''.encode('utf-8')
|
||||
self.nautilusVFSFile_table = {} # not needed in this object actually but shared
|
||||
# all over the other objects.
|
||||
|
||||
@@ -79,7 +82,7 @@ class SocketConnect(GObject.GObject):
|
||||
# print("Server command: " + cmd)
|
||||
if self.connected:
|
||||
try:
|
||||
self._sock.send(cmd.encode())
|
||||
self._sock.send(cmd.encode('utf-8'))
|
||||
except:
|
||||
print("Sending failed.")
|
||||
self.reconnect()
|
||||
@@ -94,18 +97,15 @@ class SocketConnect(GObject.GObject):
|
||||
self._sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
sock_file = os.path.join(get_runtime_dir(), appname, "socket")
|
||||
try:
|
||||
print("Socket File: " + sock_file)
|
||||
self._sock.connect(sock_file) # fails if sock_file doesn't exist
|
||||
self.connected = True
|
||||
print("Setting connected to %r." % self.connected )
|
||||
self._watch_id = GObject.io_add_watch(self._sock, GObject.IO_IN, self._handle_notify)
|
||||
print("Socket watch id: " + str(self._watch_id))
|
||||
|
||||
self.sendCommand('GET_STRINGS:\n')
|
||||
|
||||
return False # Don't run again
|
||||
except Exception as e:
|
||||
print("Could not connect to unix socket. " + str(e))
|
||||
print("Could not connect to unix socket " + sock_file + ". " + str(e))
|
||||
except Exception as e: # Bad habbit
|
||||
print("Connect could not be established, try again later.")
|
||||
self._sock.close()
|
||||
@@ -118,24 +118,24 @@ class SocketConnect(GObject.GObject):
|
||||
# Prepend the remaining data from last call
|
||||
if len(self._remainder) > 0:
|
||||
data = self._remainder + data
|
||||
self._remainder = ''.encode()
|
||||
self._remainder = ''.encode('utf-8')
|
||||
|
||||
if len(data) > 0:
|
||||
# Remember the remainder for next round
|
||||
lastNL = data.rfind('\n'.encode());
|
||||
lastNL = data.rfind('\n'.encode('utf-8'));
|
||||
if lastNL > -1 and lastNL < len(data):
|
||||
self._remainder = data[lastNL+1:]
|
||||
data = data[:lastNL]
|
||||
|
||||
for l in data.split('\n'.encode()):
|
||||
self._handle_server_response(l.decode())
|
||||
for l in data.split('\n'.encode('utf-8')):
|
||||
self._handle_server_response(l.decode('utf-8'))
|
||||
else:
|
||||
return False
|
||||
|
||||
return True # Run again
|
||||
|
||||
def _handle_server_response(self, line):
|
||||
print("Server response: " + line)
|
||||
# print("Server response: " + line)
|
||||
parts = line.split(':')
|
||||
action = parts[0]
|
||||
args = parts[1:]
|
||||
@@ -262,11 +262,11 @@ class MenuExtension(GObject.GObject, Nautilus.MenuProvider):
|
||||
|
||||
def context_menu_action(self, menu, action, file):
|
||||
filename = get_local_path(file.get_uri())
|
||||
print("Context menu: " + action + ' ' + filename)
|
||||
# print("Context menu: " + action + ' ' + filename)
|
||||
socketConnect.sendCommand(action + ":" + filename + "\n")
|
||||
|
||||
|
||||
class SyncStateExtension(GObject.GObject, Nautilus.ColumnProvider, Nautilus.InfoProvider):
|
||||
class SyncStateExtension(GObject.GObject, Nautilus.InfoProvider):
|
||||
def __init__(self):
|
||||
GObject.GObject.__init__(self)
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ static void fillFileRecordFromGetQuery(SyncJournalFileRecord &rec, SqlQuery &que
|
||||
|
||||
static QString defaultJournalMode(const QString &dbPath)
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
#if defined(Q_OS_WIN)
|
||||
// See #2693: Some exFAT file systems seem unable to cope with the
|
||||
// WAL journaling mode. They work fine with DELETE.
|
||||
QString fileSystem = FileSystem::fileSystemForPath(dbPath);
|
||||
@@ -74,6 +74,11 @@ static QString defaultJournalMode(const QString &dbPath)
|
||||
qCInfo(lcDb) << "Filesystem contains FAT - using DELETE journal mode";
|
||||
return "DELETE";
|
||||
}
|
||||
#elif defined(Q_OS_MAC)
|
||||
if (dbPath.startsWith("/Volumes/")) {
|
||||
qCInfo(lcDb) << "Mounted sync dir, do not use WAL for" << dbPath;
|
||||
return "DELETE";
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(dbPath)
|
||||
#endif
|
||||
@@ -271,6 +276,11 @@ bool SyncJournalDb::sqlFail(const QString &log, const SqlQuery &query)
|
||||
bool SyncJournalDb::checkConnect()
|
||||
{
|
||||
if (_db.isOpen()) {
|
||||
if (!QFile::exists(_dbFile)) {
|
||||
qCWarning(lcDb) << "Database open, but file file" + _dbFile + " does not exist";
|
||||
close();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
PROJECT( CrashReporter )
|
||||
cmake_policy(SET CMP0017 NEW)
|
||||
|
||||
list(APPEND crashreporter_SOURCES main.cpp)
|
||||
list(APPEND crashreporter_RC resources.qrc)
|
||||
|
||||
qt_wrap_ui( crashreporter_UI_HEADERS ${crashreporter_UI} )
|
||||
qt_add_resources( crashreporter_RC_RCC ${crashreporter_RC} )
|
||||
|
||||
|
||||
# TODO: differentiate release channel
|
||||
# if(BUILD_RELEASE)
|
||||
# set(CRASHREPORTER_RELEASE_CHANNEL "release")
|
||||
@@ -15,9 +8,30 @@ qt_add_resources( crashreporter_RC_RCC ${crashreporter_RC} )
|
||||
set(CRASHREPORTER_RELEASE_CHANNEL "nightly")
|
||||
# endif()
|
||||
|
||||
# Theme
|
||||
if(DEFINED OEM_THEME_DIR AND EXISTS "${OEM_THEME_DIR}/theme/colored")
|
||||
set(CRASHREPORTER_ICON_DIR "${OEM_THEME_DIR}/theme/colored")
|
||||
else()
|
||||
set(CRASHREPORTER_ICON_DIR "${CMAKE_SOURCE_DIR}/theme/colored")
|
||||
endif()
|
||||
|
||||
set(CRASHREPORTER_ICON_FILENAME "${APPLICATION_ICON_NAME}-icon.png")
|
||||
set(CRASHREPORTER_ICON ":/${CRASHREPORTER_ICON_FILENAME}")
|
||||
set(CRASHREPORTER_ICON_SIZE "128")
|
||||
set(CRASHREPORTER_ICON_PATH "${CRASHREPORTER_ICON_DIR}/${APPLICATION_ICON_NAME}-icon-${CRASHREPORTER_ICON_SIZE}.png")
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/resources.qrc.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/resources.qrc)
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CrashReporterConfig.h.in
|
||||
${CMAKE_CURRENT_BINARY_DIR}/CrashReporterConfig.h)
|
||||
|
||||
# Sources
|
||||
list(APPEND crashreporter_SOURCES main.cpp)
|
||||
list(APPEND crashreporter_RC "${CMAKE_CURRENT_BINARY_DIR}/resources.qrc")
|
||||
|
||||
qt_wrap_ui( crashreporter_UI_HEADERS ${crashreporter_UI} )
|
||||
qt_add_resources( crashreporter_RC_RCC ${crashreporter_RC} )
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR}
|
||||
"../3rdparty/libcrashreporter-qt/src/"
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file alias="owncloud-icon.png">../../theme/colored/owncloud-icon-128.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
5
src/crashreporter/resources.qrc.in
Normal file
5
src/crashreporter/resources.qrc.in
Normal file
@@ -0,0 +1,5 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file alias="@CRASHREPORTER_ICON_FILENAME@">@CRASHREPORTER_ICON_PATH@</file>
|
||||
</qresource>
|
||||
</RCC>
|
||||
@@ -156,7 +156,7 @@ static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
|
||||
/* First, check that the file is NOT in our tree (another file with the same name was added) */
|
||||
if (our_tree->findFile(basePath)) {
|
||||
other = nullptr;
|
||||
qCDebug(lcReconcile, "Origin found in our tree : %s", basePath.constData());
|
||||
qCInfo(lcReconcile, "Origin found in our tree : %s", basePath.constData());
|
||||
} else {
|
||||
/* Find the potential rename source file in the other tree.
|
||||
* If the renamed file could not be found in the opposite tree, that is because it
|
||||
@@ -164,7 +164,7 @@ static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
|
||||
* The journal is cleaned up later after propagation.
|
||||
*/
|
||||
other = other_tree->findFile(basePath);
|
||||
qCDebug(lcReconcile, "Rename origin in other tree (%s) %s",
|
||||
qCInfo(lcReconcile, "Rename origin in other tree (%s) %s",
|
||||
basePath.constData(), other ? "found" : "not found");
|
||||
}
|
||||
|
||||
@@ -175,7 +175,7 @@ static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
|
||||
// Some other EVAL_RENAME already claimed other.
|
||||
// We do nothing: maybe a different candidate for
|
||||
// other is found as well?
|
||||
qCDebug(lcReconcile, "Other has already been renamed to %s",
|
||||
qCInfo(lcReconcile, "Other has already been renamed to %s",
|
||||
other->rename_path.constData());
|
||||
} else if (cur->type == CSYNC_FTW_TYPE_DIR
|
||||
// The local replica is reconciled first, so the remote tree would
|
||||
@@ -187,13 +187,17 @@ static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
|
||||
|| other->instruction == CSYNC_INSTRUCTION_NONE
|
||||
|| other->instruction == CSYNC_INSTRUCTION_UPDATE_METADATA
|
||||
|| other->instruction == CSYNC_INSTRUCTION_REMOVE) {
|
||||
qCDebug(lcReconcile, "Switching %s to RENAME to %s",
|
||||
qCInfo(lcReconcile, "Switching %s to RENAME to %s",
|
||||
other->path.constData(), cur->path.constData());
|
||||
other->instruction = CSYNC_INSTRUCTION_RENAME;
|
||||
other->rename_path = cur->path;
|
||||
if( !cur->file_id.isEmpty() ) {
|
||||
other->file_id = cur->file_id;
|
||||
}
|
||||
if (ctx->current == LOCAL_REPLICA) {
|
||||
// Keep the local mtime.
|
||||
other->modtime = cur->modtime;
|
||||
}
|
||||
other->inode = cur->inode;
|
||||
cur->instruction = CSYNC_INSTRUCTION_NONE;
|
||||
// We have consumed 'other': exit this loop to not consume another one.
|
||||
@@ -207,7 +211,7 @@ static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
|
||||
// Local: The remote reconcile will be able to deal with this.
|
||||
// Remote: The local replica has already dealt with this.
|
||||
// See the EVAL_RENAME case when other was found directly.
|
||||
qCDebug(lcReconcile, "File in a renamed directory, other side's instruction: %d",
|
||||
qCInfo(lcReconcile, "File in a renamed directory, other side's instruction: %d",
|
||||
other->instruction);
|
||||
cur->instruction = CSYNC_INSTRUCTION_NONE;
|
||||
} else {
|
||||
@@ -215,7 +219,7 @@ static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
|
||||
// and the instruction in the local tree is NEW while cur has EVAL_RENAME
|
||||
// due to a remote move of the same file. In these scenarios we just
|
||||
// want the instruction to stay NEW.
|
||||
qCDebug(lcReconcile, "Other already has instruction %d",
|
||||
qCInfo(lcReconcile, "Other already has instruction %d",
|
||||
other->instruction);
|
||||
}
|
||||
};
|
||||
@@ -223,7 +227,7 @@ static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
|
||||
if (ctx->current == LOCAL_REPLICA) {
|
||||
/* use the old name to find the "other" node */
|
||||
OCC::SyncJournalFileRecord base;
|
||||
qCDebug(lcReconcile, "Finding rename origin through inode %" PRIu64 "",
|
||||
qCInfo(lcReconcile, "Finding rename origin through inode %" PRIu64 "",
|
||||
cur->inode);
|
||||
ctx->statedb->getFileRecordByInode(cur->inode, &base);
|
||||
renameCandidateProcessing(base._path);
|
||||
@@ -236,7 +240,7 @@ static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
|
||||
// line.
|
||||
auto basePath = csync_rename_adjust_full_path_source(ctx, cur->path);
|
||||
if (basePath != cur->path) {
|
||||
qCDebug(lcReconcile, "Trying rename origin by csync_rename mapping %s",
|
||||
qCInfo(lcReconcile, "Trying rename origin by csync_rename mapping %s",
|
||||
basePath.constData());
|
||||
// We go through getFileRecordsByFileId to ensure the basePath
|
||||
// computed in this way also has the expected fileid.
|
||||
@@ -249,7 +253,7 @@ static int _csync_merge_algorithm_visitor(csync_file_stat_t *cur, CSYNC * ctx) {
|
||||
|
||||
// Also feed all the other files with the same fileid if necessary
|
||||
if (!processedRename) {
|
||||
qCDebug(lcReconcile, "Finding rename origin through file ID %s",
|
||||
qCInfo(lcReconcile, "Finding rename origin through file ID %s",
|
||||
cur->file_id.constData());
|
||||
ctx->statedb->getFileRecordsByFileId(cur->file_id,
|
||||
[&](const OCC::SyncJournalFileRecord &base) { renameCandidateProcessing(base._path); });
|
||||
|
||||
@@ -130,12 +130,12 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
||||
* This code should probably be in csync_exclude, but it does not have the fs parameter.
|
||||
* Keep it here for now */
|
||||
if (ctx->ignore_hidden_files && (fs->is_hidden)) {
|
||||
qCDebug(lcUpdate, "file excluded because it is a hidden file: %s", fs->path.constData());
|
||||
qCInfo(lcUpdate, "file excluded because it is a hidden file: %s", fs->path.constData());
|
||||
excluded = CSYNC_FILE_EXCLUDE_HIDDEN;
|
||||
}
|
||||
} else {
|
||||
/* File is ignored because it's matched by a user- or system exclude pattern. */
|
||||
qCDebug(lcUpdate, "%s excluded (%d)", fs->path.constData(), excluded);
|
||||
qCInfo(lcUpdate, "%s excluded (%d)", fs->path.constData(), excluded);
|
||||
if (excluded == CSYNC_FILE_EXCLUDE_AND_REMOVE) {
|
||||
return 1;
|
||||
}
|
||||
@@ -160,7 +160,7 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
||||
*/
|
||||
QTextEncoder encoder(localCodec, QTextCodec::ConvertInvalidToNull);
|
||||
if (encoder.fromUnicode(QString::fromUtf8(fs->path)).contains('\0')) {
|
||||
qCDebug(lcUpdate, "cannot encode %s to local encoding %d",
|
||||
qCInfo(lcUpdate, "cannot encode %s to local encoding %d",
|
||||
fs->path.constData(), localCodec->mibEnum());
|
||||
excluded = CSYNC_FILE_EXCLUDE_CANNOT_ENCODE;
|
||||
}
|
||||
@@ -168,7 +168,7 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
||||
|
||||
if (fs->type == CSYNC_FTW_TYPE_FILE ) {
|
||||
if (fs->modtime == 0) {
|
||||
qCDebug(lcUpdate, "file: %s - mtime is zero!", fs->path.constData());
|
||||
qCInfo(lcUpdate, "file: %s - mtime is zero!", fs->path.constData());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,10 +195,12 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
||||
/* we have an update! */
|
||||
qCInfo(lcUpdate, "Database entry found, compare: %" PRId64 " <-> %" PRId64
|
||||
", etag: %s <-> %s, inode: %" PRId64 " <-> %" PRId64
|
||||
", size: %" PRId64 " <-> %" PRId64 ", perms: %x <-> %x, ignore: %d",
|
||||
", size: %" PRId64 " <-> %" PRId64 ", perms: %x <-> %x"
|
||||
", checksum: %s <-> %s , ignore: %d",
|
||||
((int64_t) fs->modtime), ((int64_t) base._modtime),
|
||||
fs->etag.constData(), base._etag.constData(), (uint64_t) fs->inode, (uint64_t) base._inode,
|
||||
(uint64_t) fs->size, (uint64_t) base._fileSize, *reinterpret_cast<short*>(&fs->remotePerm), *reinterpret_cast<short*>(&base._remotePerm), base._serverHasIgnoredFiles );
|
||||
(uint64_t) fs->size, (uint64_t) base._fileSize, *reinterpret_cast<short*>(&fs->remotePerm), *reinterpret_cast<short*>(&base._remotePerm), fs->checksumHeader.constData(),
|
||||
base._checksumHeader.constData(), base._serverHasIgnoredFiles);
|
||||
if (ctx->current == REMOTE_REPLICA && fs->etag != base._etag) {
|
||||
fs->instruction = CSYNC_INSTRUCTION_EVAL;
|
||||
|
||||
@@ -228,7 +230,7 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
||||
checksumIdentical = fs->checksumHeader == base._checksumHeader;
|
||||
}
|
||||
if (checksumIdentical) {
|
||||
qCDebug(lcUpdate, "NOTE: Checksums are identical, file did not actually change: %s", fs->path.constData());
|
||||
qCInfo(lcUpdate, "NOTE: Checksums are identical, file did not actually change: %s", fs->path.constData());
|
||||
fs->instruction = CSYNC_INSTRUCTION_UPDATE_METADATA;
|
||||
goto out;
|
||||
}
|
||||
@@ -252,7 +254,7 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
||||
* The metadata comparison ensure that we fetch all the file id or permission when
|
||||
* upgrading owncloud
|
||||
*/
|
||||
qCDebug(lcUpdate, "Reading from database: %s", fs->path.constData());
|
||||
qCInfo(lcUpdate, "Reading from database: %s", fs->path.constData());
|
||||
ctx->remote.read_from_db = true;
|
||||
}
|
||||
/* If it was remembered in the db that the remote dir has ignored files, store
|
||||
@@ -263,7 +265,7 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
||||
}
|
||||
if (metadata_differ) {
|
||||
/* file id or permissions has changed. Which means we need to update them in the DB. */
|
||||
qCDebug(lcUpdate, "Need to update metadata for: %s", fs->path.constData());
|
||||
qCInfo(lcUpdate, "Need to update metadata for: %s", fs->path.constData());
|
||||
fs->instruction = CSYNC_INSTRUCTION_UPDATE_METADATA;
|
||||
} else {
|
||||
fs->instruction = CSYNC_INSTRUCTION_NONE;
|
||||
@@ -271,7 +273,7 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
||||
} else {
|
||||
/* check if it's a file and has been renamed */
|
||||
if (ctx->current == LOCAL_REPLICA) {
|
||||
qCDebug(lcUpdate, "Checking for rename based on inode # %" PRId64 "", (uint64_t) fs->inode);
|
||||
qCInfo(lcUpdate, "Checking for rename based on inode # %" PRId64 "", (uint64_t) fs->inode);
|
||||
|
||||
OCC::SyncJournalFileRecord base;
|
||||
if(!ctx->statedb->getFileRecordByInode(fs->inode, &base)) {
|
||||
@@ -298,13 +300,13 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
||||
_rel_to_abs(ctx, fs->path), base._checksumHeader,
|
||||
ctx->callbacks.checksum_userdata);
|
||||
if (!fs->checksumHeader.isEmpty()) {
|
||||
qCDebug(lcUpdate, "checking checksum of potential rename %s %s <-> %s", fs->path.constData(), fs->checksumHeader.constData(), base._checksumHeader.constData());
|
||||
qCInfo(lcUpdate, "checking checksum of potential rename %s %s <-> %s", fs->path.constData(), fs->checksumHeader.constData(), base._checksumHeader.constData());
|
||||
isRename = fs->checksumHeader == base._checksumHeader;
|
||||
}
|
||||
}
|
||||
|
||||
if (isRename) {
|
||||
qCDebug(lcUpdate, "pot rename detected based on inode # %" PRId64 "", (uint64_t) fs->inode);
|
||||
qCInfo(lcUpdate, "pot rename detected based on inode # %" PRId64 "", (uint64_t) fs->inode);
|
||||
/* inode found so the file has been renamed */
|
||||
fs->instruction = CSYNC_INSTRUCTION_EVAL_RENAME;
|
||||
if (fs->type == CSYNC_FTW_TYPE_DIR) {
|
||||
@@ -314,6 +316,8 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
||||
goto out;
|
||||
|
||||
} else {
|
||||
qCInfo(lcUpdate, "Checking for rename based on fileid %s", fs->file_id.constData());
|
||||
|
||||
/* Remote Replica Rename check */
|
||||
fs->instruction = CSYNC_INSTRUCTION_NEW;
|
||||
|
||||
@@ -350,7 +354,7 @@ static int _csync_detect_update(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> f
|
||||
csync_rename_record(ctx, base._path, fs->path);
|
||||
}
|
||||
|
||||
qCDebug(lcUpdate, "remote rename detected based on fileid %s --> %s", base._path.constData(), fs->path.constData());
|
||||
qCInfo(lcUpdate, "remote rename detected based on fileid %s --> %s", base._path.constData(), fs->path.constData());
|
||||
fs->instruction = CSYNC_INSTRUCTION_EVAL_RENAME;
|
||||
done = true;
|
||||
};
|
||||
@@ -458,11 +462,11 @@ int csync_walker(CSYNC *ctx, std::unique_ptr<csync_file_stat_t> fs) {
|
||||
}
|
||||
break;
|
||||
case CSYNC_FTW_TYPE_SLINK:
|
||||
qCDebug(lcUpdate, "symlink: %s - not supported", fs->path.constData());
|
||||
qCInfo(lcUpdate, "symlink: %s - not supported", fs->path.constData());
|
||||
break;
|
||||
default:
|
||||
qCInfo(lcUpdate, "item: %s - item type %d not iterated", fs->path.constData(), fs->type);
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
|
||||
rc = _csync_detect_update(ctx, std::move(fs));
|
||||
@@ -483,7 +487,7 @@ static bool fill_tree_from_db(CSYNC *ctx, const char *uri)
|
||||
* their correct etags again and we don't run into this case.
|
||||
*/
|
||||
if( rec._etag == "_invalid_") {
|
||||
qCDebug(lcUpdate, "%s selective sync excluded", rec._path.constData());
|
||||
qCInfo(lcUpdate, "%s selective sync excluded", rec._path.constData());
|
||||
skipbase = rec._path;
|
||||
skipbase += '/';
|
||||
return;
|
||||
@@ -720,7 +724,7 @@ int csync_ftw(CSYNC *ctx, const char *uri, csync_walker_fn fn,
|
||||
}
|
||||
|
||||
csync_vio_closedir(ctx, dh);
|
||||
qCDebug(lcUpdate, " <= Closing walk for %s with read_from_db %d", uri, read_from_db);
|
||||
qCInfo(lcUpdate, " <= Closing walk for %s with read_from_db %d", uri, read_from_db);
|
||||
|
||||
return rc;
|
||||
|
||||
|
||||
@@ -136,7 +136,7 @@ IF( APPLE )
|
||||
if(SPARKLE_FOUND)
|
||||
# Define this, we need to check in updater.cpp
|
||||
add_definitions( -DHAVE_SPARKLE )
|
||||
list(APPEND updater_SRCS updater/sparkleupdater_mac.mm)
|
||||
list(APPEND updater_SRCS updater/sparkleupdater_mac.mm updater/sparkleupdater.h)
|
||||
endif()
|
||||
ENDIF()
|
||||
|
||||
|
||||
@@ -312,10 +312,10 @@ void AccountState::slotInvalidCredentials()
|
||||
|
||||
if (account()->credentials()->ready()) {
|
||||
account()->credentials()->invalidateToken();
|
||||
if (auto creds = qobject_cast<HttpCredentials *>(account()->credentials())) {
|
||||
if (creds->refreshAccessToken())
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (auto creds = qobject_cast<HttpCredentials *>(account()->credentials())) {
|
||||
if (creds->refreshAccessToken())
|
||||
return;
|
||||
}
|
||||
account()->credentials()->askFromUser();
|
||||
}
|
||||
|
||||
@@ -121,6 +121,10 @@ void Folder::checkLocalPath()
|
||||
{
|
||||
const QFileInfo fi(_definition.localPath);
|
||||
_canonicalLocalPath = fi.canonicalFilePath();
|
||||
#ifdef Q_OS_MAC
|
||||
// Workaround QTBUG-55896 (Should be fixed in Qt 5.8)
|
||||
_canonicalLocalPath = _canonicalLocalPath.normalized(QString::NormalizationForm_C);
|
||||
#endif
|
||||
if (_canonicalLocalPath.isEmpty()) {
|
||||
qCWarning(lcFolder) << "Broken symlink:" << _definition.localPath;
|
||||
_canonicalLocalPath = _definition.localPath;
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
|
||||
#include "updater/updater.h"
|
||||
#include "updater/ocupdater.h"
|
||||
#ifdef Q_OS_MAC
|
||||
// FIXME We should unify those, but Sparkle does everything behind the scene transparently
|
||||
#include "updater/sparkleupdater.h"
|
||||
#endif
|
||||
#include "ignorelisteditor.h"
|
||||
|
||||
#include "config.h"
|
||||
@@ -32,6 +36,7 @@
|
||||
#include <QNetworkProxy>
|
||||
#include <QDir>
|
||||
#include <QScopedValueRollback>
|
||||
#include <QMessageBox>
|
||||
|
||||
namespace OCC {
|
||||
|
||||
@@ -88,6 +93,18 @@ GeneralSettings::GeneralSettings(QWidget *parent)
|
||||
|
||||
// accountAdded means the wizard was finished and the wizard might change some options.
|
||||
connect(AccountManager::instance(), &AccountManager::accountAdded, this, &GeneralSettings::loadMiscSettings);
|
||||
|
||||
// Only our standard brandings currently support beta channel
|
||||
Theme *theme = Theme::instance();
|
||||
if (theme->appName() != QLatin1String("ownCloud") && theme->appName() != QLatin1String("testpilotcloud") ) {
|
||||
#ifdef Q_OS_MAC
|
||||
// Because we don't have any statusString from the SparkleUpdater anyway we can hide the whole thing
|
||||
_ui->updatesGroupBox->hide();
|
||||
#else
|
||||
_ui->updateChannelLabel->hide();
|
||||
_ui->updateChannel->hide();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
GeneralSettings::~GeneralSettings()
|
||||
@@ -117,22 +134,79 @@ void GeneralSettings::loadMiscSettings()
|
||||
|
||||
void GeneralSettings::slotUpdateInfo()
|
||||
{
|
||||
// Note: the sparkle-updater is not an OCUpdater
|
||||
OCUpdater *updater = qobject_cast<OCUpdater *>(Updater::instance());
|
||||
if (ConfigFile().skipUpdateCheck()) {
|
||||
updater = 0; // don't show update info if updates are disabled
|
||||
if (ConfigFile().skipUpdateCheck() || !Updater::instance()) {
|
||||
// updater disabled on compile
|
||||
_ui->updatesGroupBox->setVisible(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (updater) {
|
||||
connect(updater, &OCUpdater::downloadStateChanged, this, &GeneralSettings::slotUpdateInfo, Qt::UniqueConnection);
|
||||
connect(_ui->restartButton, &QAbstractButton::clicked, updater, &OCUpdater::slotStartInstaller, Qt::UniqueConnection);
|
||||
// Note: the sparkle-updater is not an OCUpdater
|
||||
OCUpdater *ocupdater = qobject_cast<OCUpdater *>(Updater::instance());
|
||||
if (ocupdater) {
|
||||
connect(ocupdater, &OCUpdater::downloadStateChanged, this, &GeneralSettings::slotUpdateInfo, Qt::UniqueConnection);
|
||||
connect(_ui->restartButton, &QAbstractButton::clicked, ocupdater, &OCUpdater::slotStartInstaller, Qt::UniqueConnection);
|
||||
connect(_ui->restartButton, &QAbstractButton::clicked, qApp, &QApplication::quit, Qt::UniqueConnection);
|
||||
_ui->updateStateLabel->setText(updater->statusString());
|
||||
_ui->restartButton->setVisible(updater->downloadState() == OCUpdater::DownloadComplete);
|
||||
} else {
|
||||
// can't have those infos from sparkle currently
|
||||
_ui->updatesGroupBox->setVisible(false);
|
||||
|
||||
_ui->updateStateLabel->setText(ocupdater->statusString());
|
||||
_ui->restartButton->setVisible(ocupdater->downloadState() == OCUpdater::DownloadComplete);
|
||||
|
||||
}
|
||||
#ifdef Q_OS_MAC
|
||||
else if (SparkleUpdater *sparkleUpdater = qobject_cast<SparkleUpdater *>(Updater::instance())) {
|
||||
_ui->updateStateLabel->setText(sparkleUpdater->statusString());
|
||||
_ui->restartButton->setVisible(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Channel selection
|
||||
_ui->updateChannel->setCurrentIndex(ConfigFile().updateChannel() == "beta" ? 1 : 0);
|
||||
connect(_ui->updateChannel, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
|
||||
this, &GeneralSettings::slotUpdateChannelChanged, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
void GeneralSettings::slotUpdateChannelChanged(int index)
|
||||
{
|
||||
QString channel = index == 0 ? QStringLiteral("stable") : QStringLiteral("beta");
|
||||
if (channel == ConfigFile().updateChannel())
|
||||
return;
|
||||
|
||||
auto msgBox = new QMessageBox(
|
||||
QMessageBox::Warning,
|
||||
tr("Change update channel?"),
|
||||
tr("The update channel determines which client updates will be offered "
|
||||
"for installation. The \"stable\" channel contains only upgrades that "
|
||||
"are considered reliable, while the versions in the \"beta\" channel "
|
||||
"may contain newer features and bugfixes, but have not yet been tested "
|
||||
"thoroughly."
|
||||
"\n\n"
|
||||
"Note that this selects only what pool upgrades are taken from, and that "
|
||||
"there are no downgrades: So going back from the beta channel to "
|
||||
"the stable channel usually cannot be done immediately and means waiting "
|
||||
"for a stable version that is newer than the currently installed beta "
|
||||
"version."),
|
||||
QMessageBox::NoButton,
|
||||
this);
|
||||
msgBox->addButton(tr("Change update channel"), QMessageBox::AcceptRole);
|
||||
msgBox->addButton(tr("Cancel"), QMessageBox::RejectRole);
|
||||
connect(msgBox, &QMessageBox::finished, msgBox, [this, channel, msgBox](int result) {
|
||||
msgBox->deleteLater();
|
||||
if (result == QMessageBox::AcceptRole) {
|
||||
ConfigFile().setUpdateChannel(channel);
|
||||
if (OCUpdater *updater = qobject_cast<OCUpdater *>(Updater::instance())) {
|
||||
updater->setUpdateUrl(Updater::updateUrl());
|
||||
updater->checkForUpdate();
|
||||
}
|
||||
#ifdef Q_OS_MAC
|
||||
else if (SparkleUpdater *updater = qobject_cast<SparkleUpdater *>(Updater::instance())) {
|
||||
updater->setUpdateUrl(Updater::updateUrl());
|
||||
updater->checkForUpdate();
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
_ui->updateChannel->setCurrentText(ConfigFile().updateChannel());
|
||||
}
|
||||
});
|
||||
msgBox->open();
|
||||
}
|
||||
|
||||
void GeneralSettings::saveMiscSettings()
|
||||
|
||||
@@ -44,6 +44,7 @@ private slots:
|
||||
void slotToggleLaunchOnStartup(bool);
|
||||
void slotToggleOptionalDesktopNotifications(bool);
|
||||
void slotUpdateInfo();
|
||||
void slotUpdateChannelChanged(int index);
|
||||
void slotIgnoreFilesEditor();
|
||||
void loadMiscSettings();
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>785</width>
|
||||
<height>523</height>
|
||||
<height>533</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -74,48 +74,88 @@
|
||||
<property name="title">
|
||||
<string>Updates</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="updateStateLabel">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="restartButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Restart && Update</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Preferred</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="updateChannelLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Channel</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>updateChannel</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="updateChannel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>stable</string>
|
||||
</property>
|
||||
</item>
|
||||
<item>
|
||||
<property name="text">
|
||||
<string>beta</string>
|
||||
</property>
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="updateStateLabel">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="openExternalLinks">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="restartButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&Restart && Update</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Preferred</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
@@ -247,7 +287,10 @@
|
||||
<tabstop>ignoredFilesButton</tabstop>
|
||||
<tabstop>newFolderLimitCheckBox</tabstop>
|
||||
<tabstop>newFolderLimitSpinBox</tabstop>
|
||||
<tabstop>newExternalStorage</tabstop>
|
||||
<tabstop>showInExplorerNavigationPaneCheckBox</tabstop>
|
||||
<tabstop>crashreporterCheckBox</tabstop>
|
||||
<tabstop>updateChannel</tabstop>
|
||||
<tabstop>restartButton</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
|
||||
@@ -50,18 +50,13 @@ const char propertyAccountC[] = "oc_account";
|
||||
ownCloudGui::ownCloudGui(Application *parent)
|
||||
: QObject(parent)
|
||||
, _tray(0)
|
||||
,
|
||||
#if defined(Q_OS_MAC)
|
||||
_settingsDialog(new SettingsDialogMac(this))
|
||||
,
|
||||
, _settingsDialog(new SettingsDialogMac(this))
|
||||
#else
|
||||
_settingsDialog(new SettingsDialog(this))
|
||||
,
|
||||
, _settingsDialog(new SettingsDialog(this))
|
||||
#endif
|
||||
_logBrowser(0)
|
||||
, _contextMenuVisibleOsx(false)
|
||||
, _logBrowser(0)
|
||||
, _recentActionsMenu(0)
|
||||
, _qdbusmenuWorkaround(false)
|
||||
, _app(parent)
|
||||
{
|
||||
_tray = new Systray();
|
||||
@@ -117,7 +112,7 @@ void ownCloudGui::slotOpenSettingsDialog()
|
||||
|
||||
void ownCloudGui::slotTrayClicked(QSystemTrayIcon::ActivationReason reason)
|
||||
{
|
||||
if (_qdbusmenuWorkaround) {
|
||||
if (_workaroundFakeDoubleClick) {
|
||||
static QElapsedTimer last_click;
|
||||
if (last_click.isValid() && last_click.elapsed() < 200) {
|
||||
return;
|
||||
@@ -381,17 +376,19 @@ void ownCloudGui::addAccountContextMenu(AccountStatePtr accountState, QMenu *men
|
||||
|
||||
void ownCloudGui::slotContextMenuAboutToShow()
|
||||
{
|
||||
// For some reason on OS X _contextMenu->isVisible returns always false
|
||||
_contextMenuVisibleOsx = true;
|
||||
_contextMenuVisibleManual = true;
|
||||
|
||||
// Update icon in sys tray, as it might change depending on the context menu state
|
||||
slotComputeOverallSyncStatus();
|
||||
|
||||
if (!_workaroundNoAboutToShowUpdate) {
|
||||
updateContextMenu();
|
||||
}
|
||||
}
|
||||
|
||||
void ownCloudGui::slotContextMenuAboutToHide()
|
||||
{
|
||||
// For some reason on OS X _contextMenu->isVisible returns always false
|
||||
_contextMenuVisibleOsx = false;
|
||||
_contextMenuVisibleManual = false;
|
||||
|
||||
// Update icon in sys tray, as it might change depending on the context menu state
|
||||
slotComputeOverallSyncStatus();
|
||||
@@ -399,11 +396,11 @@ void ownCloudGui::slotContextMenuAboutToHide()
|
||||
|
||||
bool ownCloudGui::contextMenuVisible() const
|
||||
{
|
||||
#ifdef Q_OS_MAC
|
||||
return _contextMenuVisibleOsx;
|
||||
#else
|
||||
// On some platforms isVisible doesn't work and always returns false,
|
||||
// elsewhere aboutToHide is unreliable.
|
||||
if (_workaroundManualVisibility)
|
||||
return _contextMenuVisibleManual;
|
||||
return _contextMenu->isVisible();
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool minimalTrayMenu()
|
||||
@@ -426,12 +423,36 @@ static bool updateWhileVisible()
|
||||
}
|
||||
}
|
||||
|
||||
static QByteArray forceQDBusTrayWorkaround()
|
||||
static QByteArray envForceQDBusTrayWorkaround()
|
||||
{
|
||||
static QByteArray var = qgetenv("OWNCLOUD_FORCE_QDBUS_TRAY_WORKAROUND");
|
||||
return var;
|
||||
}
|
||||
|
||||
static QByteArray envForceWorkaroundShowAndHideTray()
|
||||
{
|
||||
static QByteArray var = qgetenv("OWNCLOUD_FORCE_TRAY_SHOW_HIDE");
|
||||
return var;
|
||||
}
|
||||
|
||||
static QByteArray envForceWorkaroundNoAboutToShowUpdate()
|
||||
{
|
||||
static QByteArray var = qgetenv("OWNCLOUD_FORCE_TRAY_NO_ABOUT_TO_SHOW");
|
||||
return var;
|
||||
}
|
||||
|
||||
static QByteArray envForceWorkaroundFakeDoubleClick()
|
||||
{
|
||||
static QByteArray var = qgetenv("OWNCLOUD_FORCE_TRAY_FAKE_DOUBLE_CLICK");
|
||||
return var;
|
||||
}
|
||||
|
||||
static QByteArray envForceWorkaroundManualVisibility()
|
||||
{
|
||||
static QByteArray var = qgetenv("OWNCLOUD_FORCE_TRAY_MANUAL_VISIBILITY");
|
||||
return var;
|
||||
}
|
||||
|
||||
void ownCloudGui::setupContextMenu()
|
||||
{
|
||||
if (_contextMenu) {
|
||||
@@ -454,51 +475,65 @@ void ownCloudGui::setupContextMenu()
|
||||
return;
|
||||
}
|
||||
|
||||
// Enables workarounds for bugs introduced in Qt 5.5.0
|
||||
// In particular QTBUG-47863 #3672 (tray menu fails to update and
|
||||
// becomes unresponsive) and QTBUG-48068 #3722 (click signal is
|
||||
// emitted several times)
|
||||
// The Qt version check intentionally uses 5.0.0 (where platformMenu()
|
||||
// was introduced) instead of 5.5.0 to avoid issues where the Qt
|
||||
// version used to build is different from the one used at runtime.
|
||||
// If we build with 5.6.1 or newer, we can skip this because the
|
||||
// bugs should be fixed there.
|
||||
#ifdef Q_OS_LINUX
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)) && (QT_VERSION < QT_VERSION_CHECK(5, 6, 0))
|
||||
if (qVersion() == QByteArray("5.5.0")) {
|
||||
QObject *platformMenu = reinterpret_cast<QObject *>(_tray->contextMenu()->platformMenu());
|
||||
if (platformMenu
|
||||
&& platformMenu->metaObject()->className() == QLatin1String("QDBusPlatformMenu")) {
|
||||
_qdbusmenuWorkaround = true;
|
||||
qCWarning(lcApplication) << "Enabled QDBusPlatformMenu workaround";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
auto applyEnvVariable = [](bool *sw, const QByteArray &value) {
|
||||
if (value == "1")
|
||||
*sw = true;
|
||||
if (value == "0")
|
||||
*sw = false;
|
||||
};
|
||||
|
||||
if (forceQDBusTrayWorkaround() == "1") {
|
||||
_qdbusmenuWorkaround = true;
|
||||
} else if (forceQDBusTrayWorkaround() == "0") {
|
||||
_qdbusmenuWorkaround = false;
|
||||
// This is an old compound flag that people might still depend on
|
||||
bool qdbusmenuWorkarounds = false;
|
||||
applyEnvVariable(&qdbusmenuWorkarounds, envForceQDBusTrayWorkaround());
|
||||
if (qdbusmenuWorkarounds) {
|
||||
_workaroundFakeDoubleClick = true;
|
||||
_workaroundNoAboutToShowUpdate = true;
|
||||
_workaroundShowAndHideTray = true;
|
||||
}
|
||||
|
||||
// When the qdbusmenuWorkaround is necessary, we can't do on-demand updates
|
||||
// because the workaround is to hide and show the tray icon.
|
||||
if (_qdbusmenuWorkaround) {
|
||||
connect(&_workaroundBatchTrayUpdate, &QTimer::timeout, this, &ownCloudGui::updateContextMenu);
|
||||
_workaroundBatchTrayUpdate.setInterval(30 * 1000);
|
||||
_workaroundBatchTrayUpdate.setSingleShot(true);
|
||||
} else {
|
||||
// Update the context menu whenever we're about to show it
|
||||
// to the user.
|
||||
#ifdef Q_OS_MAC
|
||||
// https://bugreports.qt.io/browse/QTBUG-54633
|
||||
connect(_contextMenu.data(), SIGNAL(aboutToShow()), SLOT(slotContextMenuAboutToShow()));
|
||||
connect(_contextMenu.data(), SIGNAL(aboutToHide()), SLOT(slotContextMenuAboutToHide()));
|
||||
#else
|
||||
connect(_contextMenu.data(), &QMenu::aboutToShow, this, &ownCloudGui::updateContextMenu);
|
||||
// https://bugreports.qt.io/browse/QTBUG-54633
|
||||
_workaroundNoAboutToShowUpdate = true;
|
||||
_workaroundManualVisibility = true;
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_LINUX
|
||||
// For KDE sessions if the platform plugin is missing,
|
||||
// neither aboutToShow() updates nor the isVisible() call
|
||||
// work. At least aboutToHide is reliable.
|
||||
// https://github.com/owncloud/client/issues/6545
|
||||
static QByteArray xdgCurrentDesktop = qgetenv("XDG_CURRENT_DESKTOP");
|
||||
static QByteArray desktopSession = qgetenv("DESKTOP_SESSION");
|
||||
bool isKde =
|
||||
xdgCurrentDesktop.contains("KDE")
|
||||
|| desktopSession.contains("plasma")
|
||||
|| desktopSession.contains("kde");
|
||||
QObject *platformMenu = reinterpret_cast<QObject *>(_tray->contextMenu()->platformMenu());
|
||||
if (isKde && platformMenu && platformMenu->metaObject()->className() == QLatin1String("QDBusPlatformMenu")) {
|
||||
_workaroundManualVisibility = true;
|
||||
_workaroundNoAboutToShowUpdate = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
applyEnvVariable(&_workaroundNoAboutToShowUpdate, envForceWorkaroundNoAboutToShowUpdate());
|
||||
applyEnvVariable(&_workaroundFakeDoubleClick, envForceWorkaroundFakeDoubleClick());
|
||||
applyEnvVariable(&_workaroundShowAndHideTray, envForceWorkaroundShowAndHideTray());
|
||||
applyEnvVariable(&_workaroundManualVisibility, envForceWorkaroundManualVisibility());
|
||||
|
||||
qCInfo(lcApplication) << "Tray menu workarounds:"
|
||||
<< "noabouttoshow:" << _workaroundNoAboutToShowUpdate
|
||||
<< "fakedoubleclick:" << _workaroundFakeDoubleClick
|
||||
<< "showhide:" << _workaroundShowAndHideTray
|
||||
<< "manualvisibility:" << _workaroundManualVisibility;
|
||||
|
||||
|
||||
connect(&_delayedTrayUpdateTimer, &QTimer::timeout, this, &ownCloudGui::updateContextMenu);
|
||||
_delayedTrayUpdateTimer.setInterval(2 * 1000);
|
||||
_delayedTrayUpdateTimer.setSingleShot(true);
|
||||
|
||||
connect(_contextMenu.data(), SIGNAL(aboutToShow()), SLOT(slotContextMenuAboutToShow()));
|
||||
// unfortunately aboutToHide is unreliable, it seems to work on OSX though
|
||||
connect(_contextMenu.data(), SIGNAL(aboutToHide()), SLOT(slotContextMenuAboutToHide()));
|
||||
|
||||
// Populate the context menu now.
|
||||
updateContextMenu();
|
||||
@@ -510,13 +545,21 @@ void ownCloudGui::updateContextMenu()
|
||||
return;
|
||||
}
|
||||
|
||||
if (_qdbusmenuWorkaround) {
|
||||
// If it's visible, we can't update live, and it won't be updated lazily: reschedule
|
||||
if (contextMenuVisible() && !updateWhileVisible() && _workaroundNoAboutToShowUpdate) {
|
||||
if (!_delayedTrayUpdateTimer.isActive()) {
|
||||
_delayedTrayUpdateTimer.start();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (_workaroundShowAndHideTray) {
|
||||
// To make tray menu updates work with these bugs (see setupContextMenu)
|
||||
// we need to hide and show the tray icon. We don't want to do that
|
||||
// while it's visible!
|
||||
if (contextMenuVisible()) {
|
||||
if (!_workaroundBatchTrayUpdate.isActive()) {
|
||||
_workaroundBatchTrayUpdate.start();
|
||||
if (!_delayedTrayUpdateTimer.isActive()) {
|
||||
_delayedTrayUpdateTimer.start();
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -631,35 +674,30 @@ void ownCloudGui::updateContextMenu()
|
||||
}
|
||||
_contextMenu->addAction(_actionQuit);
|
||||
|
||||
if (_qdbusmenuWorkaround) {
|
||||
if (_workaroundShowAndHideTray) {
|
||||
_tray->show();
|
||||
}
|
||||
}
|
||||
|
||||
void ownCloudGui::updateContextMenuNeeded()
|
||||
{
|
||||
// For the workaround case updating while visible is impossible. Instead
|
||||
// occasionally update the menu when it's invisible.
|
||||
if (_qdbusmenuWorkaround) {
|
||||
if (!_workaroundBatchTrayUpdate.isActive()) {
|
||||
_workaroundBatchTrayUpdate.start();
|
||||
}
|
||||
// if it's visible and we can update live: update now
|
||||
if (contextMenuVisible() && updateWhileVisible()) {
|
||||
// Note: don't update while visible on OSX
|
||||
// https://bugreports.qt.io/browse/QTBUG-54845
|
||||
updateContextMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
// https://bugreports.qt.io/browse/QTBUG-54845
|
||||
// We cannot update on demand or while visible -> update when invisible.
|
||||
if (!contextMenuVisible()) {
|
||||
updateContextMenu();
|
||||
// if we can't lazily update: update later
|
||||
if (_workaroundNoAboutToShowUpdate) {
|
||||
// Note: don't update immediately even in the invisible case
|
||||
// as that can lead to extremely frequent menu updates
|
||||
if (!_delayedTrayUpdateTimer.isActive()) {
|
||||
_delayedTrayUpdateTimer.start();
|
||||
}
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if (updateWhileVisible() && contextMenuVisible())
|
||||
updateContextMenu();
|
||||
#endif
|
||||
|
||||
// If no update was done here, we might update it on-demand due to
|
||||
// the aboutToShow() signal.
|
||||
}
|
||||
|
||||
void ownCloudGui::slotShowTrayMessage(const QString &title, const QString &msg)
|
||||
|
||||
@@ -119,14 +119,19 @@ private:
|
||||
// tray's menu
|
||||
QScopedPointer<QMenu> _contextMenu;
|
||||
|
||||
// Manually tracking whether the context menu is visible, but only works
|
||||
// on OSX because aboutToHide is not reliable everywhere.
|
||||
bool _contextMenuVisibleOsx;
|
||||
// Manually tracking whether the context menu is visible via aboutToShow
|
||||
// and aboutToHide. Unfortunately aboutToHide isn't reliable everywhere
|
||||
// so this only gets used with _workaroundManualVisibility (when the tray's
|
||||
// isVisible() is unreliable)
|
||||
bool _contextMenuVisibleManual = false;
|
||||
|
||||
QMenu *_recentActionsMenu;
|
||||
QVector<QMenu *> _accountMenus;
|
||||
bool _qdbusmenuWorkaround;
|
||||
QTimer _workaroundBatchTrayUpdate;
|
||||
bool _workaroundShowAndHideTray = false;
|
||||
bool _workaroundNoAboutToShowUpdate = false;
|
||||
bool _workaroundFakeDoubleClick = false;
|
||||
bool _workaroundManualVisibility = false;
|
||||
QTimer _delayedTrayUpdateTimer;
|
||||
QMap<QString, QPointer<ShareDialog>> _shareDialogs;
|
||||
|
||||
QAction *_actionLogin;
|
||||
|
||||
@@ -183,6 +183,10 @@ SocketApi::SocketApi(QObject *parent)
|
||||
// Example for developer builds (with ad-hoc signing identity): "" "com.owncloud.desktopclient" ".socketApi"
|
||||
// Example for official signed packages: "9B5WD74GWJ." "com.owncloud.desktopclient" ".socketApi"
|
||||
socketPath = SOCKETAPI_TEAM_IDENTIFIER_PREFIX APPLICATION_REV_DOMAIN ".socketApi";
|
||||
#ifdef Q_OS_MAC
|
||||
// Tell Finder to use the Extension (checking it from System Preferences -> Extensions)
|
||||
system("pluginkit -e use -i " APPLICATION_REV_DOMAIN ".FinderSyncExt &");
|
||||
#endif
|
||||
} else if (Utility::isLinux() || Utility::isBSD()) {
|
||||
QString runtimeDir;
|
||||
runtimeDir = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation);
|
||||
@@ -220,6 +224,11 @@ SocketApi::~SocketApi()
|
||||
// All remaining sockets will be destroyed with _localServer, their parent
|
||||
ASSERT(_listeners.isEmpty() || _listeners.first().socket->parent() == &_localServer);
|
||||
_listeners.clear();
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
// Unload the extension (uncheck from System Preferences -> Extensions)
|
||||
system("pluginkit -e ignore -i " APPLICATION_REV_DOMAIN ".FinderSyncExt &");
|
||||
#endif
|
||||
}
|
||||
|
||||
void SocketApi::slotNewConnection()
|
||||
|
||||
@@ -204,6 +204,10 @@ void SslButton::slotUpdateMenu()
|
||||
|
||||
AccountPtr account = _accountState->account();
|
||||
|
||||
if (account->isHttp2Supported()) {
|
||||
_menu->addAction("HTTP/2")->setEnabled(false);
|
||||
}
|
||||
|
||||
if (account->url().scheme() == QLatin1String("https")) {
|
||||
QString sslVersion = account->_sessionCipher.protocolString()
|
||||
+ ", " + account->_sessionCipher.authenticationMethod()
|
||||
|
||||
@@ -92,6 +92,11 @@ OCUpdater::OCUpdater(const QUrl &url)
|
||||
{
|
||||
}
|
||||
|
||||
void OCUpdater::setUpdateUrl(const QUrl &url)
|
||||
{
|
||||
_updateUrl = url;
|
||||
}
|
||||
|
||||
bool OCUpdater::performUpdate()
|
||||
{
|
||||
ConfigFile cfg;
|
||||
@@ -179,6 +184,81 @@ void OCUpdater::setDownloadState(DownloadState state)
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
// Following functions are taken from https://github.com/qt/qtbase/blob/5.8/src/corelib/io/qprocess_win.cpp
|
||||
// to make use of this fix https://github.com/qt/qtbase/commit/bec2fc19fd18768b16925597871c77a61e716abd
|
||||
// for QTBUG-53833: Without this we get an ugly powershell window on update. In 2.5/master we use Qt 5.10
|
||||
// which obviously already has the fix.
|
||||
static QString qt_create_commandline(const QString &program, const QStringList &arguments)
|
||||
{
|
||||
QString args;
|
||||
if (!program.isEmpty()) {
|
||||
QString programName = program;
|
||||
if (!programName.startsWith(QLatin1Char('\"')) && !programName.endsWith(QLatin1Char('\"')) && programName.contains(QLatin1Char(' ')))
|
||||
programName = QLatin1Char('\"') + programName + QLatin1Char('\"');
|
||||
programName.replace(QLatin1Char('/'), QLatin1Char('\\'));
|
||||
|
||||
// add the prgram as the first arg ... it works better
|
||||
args = programName + QLatin1Char(' ');
|
||||
}
|
||||
|
||||
for (int i=0; i<arguments.size(); ++i) {
|
||||
QString tmp = arguments.at(i);
|
||||
// Quotes are escaped and their preceding backslashes are doubled.
|
||||
tmp.replace(QRegExp(QLatin1String("(\\\\*)\"")), QLatin1String("\\1\\1\\\""));
|
||||
if (tmp.isEmpty() || tmp.contains(QLatin1Char(' ')) || tmp.contains(QLatin1Char('\t'))) {
|
||||
// The argument must not end with a \ since this would be interpreted
|
||||
// as escaping the quote -- rather put the \ behind the quote: e.g.
|
||||
// rather use "foo"\ than "foo\"
|
||||
int i = tmp.length();
|
||||
while (i > 0 && tmp.at(i - 1) == QLatin1Char('\\'))
|
||||
--i;
|
||||
tmp.insert(i, QLatin1Char('"'));
|
||||
tmp.prepend(QLatin1Char('"'));
|
||||
}
|
||||
args += QLatin1Char(' ') + tmp;
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDir = QString(), qint64 *pid = 0)
|
||||
{
|
||||
// static const DWORD errorElevationRequired = 740;
|
||||
|
||||
QString args = qt_create_commandline(program, arguments);
|
||||
bool success = false;
|
||||
PROCESS_INFORMATION pinfo;
|
||||
|
||||
DWORD dwCreationFlags = (GetConsoleWindow() ? 0 : CREATE_NO_WINDOW);
|
||||
dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
|
||||
STARTUPINFOW startupInfo = { sizeof( STARTUPINFO ), 0, 0, 0,
|
||||
(ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
|
||||
(ulong)CW_USEDEFAULT, (ulong)CW_USEDEFAULT,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
success = CreateProcess(0, (wchar_t*)args.utf16(),
|
||||
0, 0, FALSE, dwCreationFlags, 0,
|
||||
workingDir.isEmpty() ? 0 : (wchar_t*)workingDir.utf16(),
|
||||
&startupInfo, &pinfo);
|
||||
|
||||
// if (success) {
|
||||
CloseHandle(pinfo.hThread);
|
||||
CloseHandle(pinfo.hProcess);
|
||||
if (pid)
|
||||
*pid = pinfo.dwProcessId;
|
||||
// } else if (GetLastError() == errorElevationRequired) {
|
||||
// success = startDetachedUacPrompt(program, arguments, workingDir, pid);
|
||||
// }
|
||||
|
||||
return success;
|
||||
}
|
||||
#else
|
||||
bool startDetached(const QString &program, const QStringList &arguments, const QString &workingDir = QString(), qint64 *pid = 0)
|
||||
{
|
||||
return QProcess::startDetached(program, arguments, workingDir, pid);
|
||||
}
|
||||
#endif
|
||||
|
||||
void OCUpdater::slotStartInstaller()
|
||||
{
|
||||
ConfigFile cfg;
|
||||
@@ -187,8 +267,30 @@ void OCUpdater::slotStartInstaller()
|
||||
settings.setValue(autoUpdateAttemptedC, true);
|
||||
settings.sync();
|
||||
qCInfo(lcUpdater) << "Running updater" << updateFile;
|
||||
QProcess::startDetached(updateFile, QStringList() << "/S"
|
||||
<< "/launch");
|
||||
|
||||
if(updateFile.endsWith(".exe")) {
|
||||
QProcess::startDetached(updateFile, QStringList() << "/S"
|
||||
<< "/launch");
|
||||
} else if(updateFile.endsWith(".msi")) {
|
||||
// When MSIs are installed without gui they cannot launch applications
|
||||
// as they lack the user context. That is why we need to run the client
|
||||
// manually here. We wrap the msiexec and client invocation in a powershell
|
||||
// script because owncloud.exe will be shut down for installation.
|
||||
// | Out-Null forces powershell to wait for msiexec to finish.
|
||||
auto preparePathForPowershell = [](QString path) {
|
||||
path.replace("'", "''");
|
||||
|
||||
return QDir::toNativeSeparators(path);
|
||||
};
|
||||
|
||||
auto msiLogFile = cfg.configPath() + "msi.log";
|
||||
QString command = QString("&{msiexec /norestart /passive /i '%1' /L*V '%2'| Out-Null ; &'%3'}")
|
||||
.arg(preparePathForPowershell(updateFile))
|
||||
.arg(preparePathForPowershell(msiLogFile))
|
||||
.arg(preparePathForPowershell(QCoreApplication::applicationFilePath()));
|
||||
|
||||
startDetached("powershell.exe", QStringList{"-Command", command});
|
||||
}
|
||||
}
|
||||
|
||||
void OCUpdater::checkForUpdate()
|
||||
@@ -298,7 +400,7 @@ void NSISUpdater::versionInfoArrived(const UpdateInfo &info)
|
||||
showDialog(info);
|
||||
}
|
||||
if (!url.isEmpty()) {
|
||||
_targetFile = cfg.configPath() + url.mid(url.lastIndexOf('/'));
|
||||
_targetFile = cfg.configPath() + url.mid(url.lastIndexOf('/')+1);
|
||||
if (QFile(_targetFile).exists()) {
|
||||
setDownloadState(DownloadComplete);
|
||||
} else {
|
||||
|
||||
@@ -99,6 +99,8 @@ public:
|
||||
UpdateOnlyAvailableThroughSystem };
|
||||
explicit OCUpdater(const QUrl &url);
|
||||
|
||||
void setUpdateUrl(const QUrl &url);
|
||||
|
||||
bool performUpdate();
|
||||
|
||||
void checkForUpdate() Q_DECL_OVERRIDE;
|
||||
|
||||
@@ -23,15 +23,20 @@ namespace OCC {
|
||||
|
||||
class SparkleUpdater : public Updater
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
SparkleUpdater(const QString &appCastUrl);
|
||||
SparkleUpdater(const QUrl &appCastUrl);
|
||||
~SparkleUpdater();
|
||||
|
||||
void setUpdateUrl(const QUrl &url);
|
||||
|
||||
// unused in this updater
|
||||
void checkForUpdate() Q_DECL_OVERRIDE;
|
||||
void backgroundCheckForUpdate() Q_DECL_OVERRIDE;
|
||||
bool handleStartup() Q_DECL_OVERRIDE { return false; }
|
||||
|
||||
QString statusString();
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *d;
|
||||
|
||||
@@ -73,7 +73,7 @@ class SparkleUpdater::Private
|
||||
};
|
||||
|
||||
// Delete ~/Library//Preferences/com.owncloud.desktopclient.plist to re-test
|
||||
SparkleUpdater::SparkleUpdater(const QString& appCastUrl)
|
||||
SparkleUpdater::SparkleUpdater(const QUrl& appCastUrl)
|
||||
: Updater()
|
||||
{
|
||||
d = new Private;
|
||||
@@ -89,9 +89,7 @@ SparkleUpdater::SparkleUpdater(const QString& appCastUrl)
|
||||
[d->updater resetUpdateCycle];
|
||||
[d->updater retain];
|
||||
|
||||
NSURL* url = [NSURL URLWithString:
|
||||
[NSString stringWithUTF8String: appCastUrl.toUtf8().data()]];
|
||||
[d->updater setFeedURL: url];
|
||||
setUpdateUrl(appCastUrl);
|
||||
|
||||
// Sparkle 1.8 required
|
||||
NSString *userAgent = [NSString stringWithUTF8String: Utility::userAgentString().data()];
|
||||
@@ -104,7 +102,14 @@ SparkleUpdater::~SparkleUpdater()
|
||||
delete d;
|
||||
}
|
||||
|
||||
void SparkleUpdater::setUpdateUrl(const QUrl &url)
|
||||
{
|
||||
NSURL* nsurl = [NSURL URLWithString:
|
||||
[NSString stringWithUTF8String: url.toString().toUtf8().data()]];
|
||||
[d->updater setFeedURL: nsurl];
|
||||
}
|
||||
|
||||
// FIXME: Should be changed to not instanicate the SparkleUpdater at all in this case
|
||||
bool autoUpdaterAllowed()
|
||||
{
|
||||
// See https://github.com/owncloud/client/issues/2931
|
||||
@@ -133,4 +138,10 @@ void SparkleUpdater::backgroundCheckForUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
QString SparkleUpdater::statusString()
|
||||
{
|
||||
// FIXME Show the real state depending on the callbacks
|
||||
return QString();
|
||||
}
|
||||
|
||||
} // namespace OCC
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "theme.h"
|
||||
#include "common/utility.h"
|
||||
#include "version.h"
|
||||
#include "configfile.h"
|
||||
|
||||
#include "config.h"
|
||||
|
||||
@@ -39,6 +40,29 @@ Updater *Updater::instance()
|
||||
return _instance;
|
||||
}
|
||||
|
||||
QUrl Updater::updateUrl()
|
||||
{
|
||||
QUrl updateBaseUrl(QString::fromLocal8Bit(qgetenv("OCC_UPDATE_URL")));
|
||||
if (updateBaseUrl.isEmpty()) {
|
||||
updateBaseUrl = QUrl(QLatin1String(APPLICATION_UPDATE_URL));
|
||||
}
|
||||
if (!updateBaseUrl.isValid() || updateBaseUrl.host() == ".") {
|
||||
return QUrl();
|
||||
}
|
||||
|
||||
auto url = addQueryParams(updateBaseUrl);
|
||||
|
||||
#if defined(Q_OS_MAC) && defined(HAVE_SPARKLE)
|
||||
url.addQueryItem(QLatin1String("sparkle"), QLatin1String("true"));
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_WIN)
|
||||
url.addQueryItem(QLatin1String("msi"), QLatin1String("true"));
|
||||
#endif
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
QUrl Updater::addQueryParams(const QUrl &url)
|
||||
{
|
||||
QUrl paramUrl = url;
|
||||
@@ -64,14 +88,10 @@ QUrl Updater::addQueryParams(const QUrl &url)
|
||||
|
||||
QString suffix = QString::fromLatin1(MIRALL_STRINGIFY(MIRALL_VERSION_SUFFIX));
|
||||
paramUrl.addQueryItem(QLatin1String("versionsuffix"), suffix);
|
||||
if (suffix.startsWith("daily")
|
||||
|| suffix.startsWith("nightly")
|
||||
|| suffix.startsWith("alpha")
|
||||
|| suffix.startsWith("rc")
|
||||
|| suffix.startsWith("beta")) {
|
||||
paramUrl.addQueryItem(QLatin1String("channel"), "beta");
|
||||
// FIXME: Provide a checkbox in UI to enable regular versions to switch
|
||||
// to beta channel
|
||||
|
||||
auto channel = ConfigFile().updateChannel();
|
||||
if (channel != "stable") {
|
||||
paramUrl.addQueryItem(QLatin1String("channel"), channel);
|
||||
}
|
||||
|
||||
return paramUrl;
|
||||
@@ -98,23 +118,18 @@ QString Updater::getSystemInfo()
|
||||
// To test, cmake with -DAPPLICATION_UPDATE_URL="http://127.0.0.1:8080/test.rss"
|
||||
Updater *Updater::create()
|
||||
{
|
||||
QUrl updateBaseUrl(QString::fromLocal8Bit(qgetenv("OCC_UPDATE_URL")));
|
||||
if (updateBaseUrl.isEmpty()) {
|
||||
updateBaseUrl = QUrl(QLatin1String(APPLICATION_UPDATE_URL));
|
||||
}
|
||||
if (!updateBaseUrl.isValid() || updateBaseUrl.host() == ".") {
|
||||
auto url = updateUrl();
|
||||
if (url.isEmpty()) {
|
||||
qCWarning(lcUpdater) << "Not a valid updater URL, will not do update check";
|
||||
return 0;
|
||||
}
|
||||
updateBaseUrl = addQueryParams(updateBaseUrl);
|
||||
#if defined(Q_OS_MAC) && defined(HAVE_SPARKLE)
|
||||
updateBaseUrl.addQueryItem(QLatin1String("sparkle"), QLatin1String("true"));
|
||||
return new SparkleUpdater(updateBaseUrl.toString());
|
||||
return new SparkleUpdater(url);
|
||||
#elif defined(Q_OS_WIN32)
|
||||
// the best we can do is notify about updates
|
||||
return new NSISUpdater(updateBaseUrl);
|
||||
return new NSISUpdater(url);
|
||||
#else
|
||||
return new PassiveUpdateNotifier(QUrl(updateBaseUrl));
|
||||
return new PassiveUpdateNotifier(url);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ public:
|
||||
};
|
||||
|
||||
static Updater *instance();
|
||||
static QUrl updateUrl();
|
||||
|
||||
virtual void checkForUpdate() = 0;
|
||||
virtual void backgroundCheckForUpdate() = 0;
|
||||
|
||||
@@ -218,7 +218,7 @@ public:
|
||||
/** Detects a specific bug in older server versions */
|
||||
bool rootEtagChangesNotOnlySubFolderEtags();
|
||||
|
||||
/** True when the server supports HTTP2 */
|
||||
/** True when the server connection is using HTTP2 */
|
||||
bool isHttp2Supported() { return _http2Supported; }
|
||||
void setHttp2Supported(bool value) { _http2Supported = value; }
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "theme.h"
|
||||
#include "common/utility.h"
|
||||
#include "common/asserts.h"
|
||||
#include "version.h"
|
||||
|
||||
#include "creds/abstractcredentials.h"
|
||||
|
||||
@@ -52,6 +53,7 @@ static const char crashReporterC[] = "crashReporter";
|
||||
static const char optionalDesktopNoficationsC[] = "optionalDesktopNotifications";
|
||||
static const char skipUpdateCheckC[] = "skipUpdateCheck";
|
||||
static const char updateCheckIntervalC[] = "updateCheckInterval";
|
||||
static const char updateChannelC[] = "updateChannel";
|
||||
static const char geometryC[] = "geometry";
|
||||
static const char timeoutC[] = "timeout";
|
||||
static const char chunkSizeC[] = "chunkSize";
|
||||
@@ -468,6 +470,28 @@ void ConfigFile::setSkipUpdateCheck(bool skip, const QString &connection)
|
||||
settings.sync();
|
||||
}
|
||||
|
||||
QString ConfigFile::updateChannel() const
|
||||
{
|
||||
QString defaultUpdateChannel = QStringLiteral("stable");
|
||||
QString suffix = QString::fromLatin1(MIRALL_STRINGIFY(MIRALL_VERSION_SUFFIX));
|
||||
if (suffix.startsWith("daily")
|
||||
|| suffix.startsWith("nightly")
|
||||
|| suffix.startsWith("alpha")
|
||||
|| suffix.startsWith("rc")
|
||||
|| suffix.startsWith("beta")) {
|
||||
defaultUpdateChannel = QStringLiteral("beta");
|
||||
}
|
||||
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
return settings.value(QLatin1String(updateChannelC), defaultUpdateChannel).toString();
|
||||
}
|
||||
|
||||
void ConfigFile::setUpdateChannel(const QString &channel)
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
settings.setValue(QLatin1String(updateChannelC), channel);
|
||||
}
|
||||
|
||||
int ConfigFile::maxLogLines() const
|
||||
{
|
||||
QSettings settings(configFile(), QSettings::IniFormat);
|
||||
|
||||
@@ -131,6 +131,9 @@ public:
|
||||
bool skipUpdateCheck(const QString &connection = QString()) const;
|
||||
void setSkipUpdateCheck(bool, const QString &);
|
||||
|
||||
QString updateChannel() const;
|
||||
void setUpdateChannel(const QString &channel);
|
||||
|
||||
void saveGeometryHeader(QHeaderView *header);
|
||||
void restoreGeometryHeader(QHeaderView *header);
|
||||
|
||||
|
||||
@@ -117,6 +117,7 @@ HttpCredentials::HttpCredentials(const QString &user, const QString &password, c
|
||||
, _clientSslKey(key)
|
||||
, _clientSslCertificate(certificate)
|
||||
, _keychainMigration(false)
|
||||
, _retryOnKeyChainError(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -219,6 +220,21 @@ void HttpCredentials::deleteOldKeychainEntries()
|
||||
|
||||
void HttpCredentials::slotReadClientCertPEMJobDone(QKeychain::Job *incoming)
|
||||
{
|
||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
|
||||
Q_ASSERT(!incoming->insecureFallback()); // If insecureFallback is set, the next test would be pointless
|
||||
if (_retryOnKeyChainError && (incoming->error() == QKeychain::NoBackendAvailable
|
||||
|| incoming->error() == QKeychain::OtherError)) {
|
||||
// Could be that the backend was not yet available. Wait some extra seconds.
|
||||
// (Issues #4274 and #6522)
|
||||
// (For kwallet, the error is OtherError instead of NoBackendAvailable, maybe a bug in QtKeychain)
|
||||
qCInfo(lcHttpCredentials) << "Backend unavailable (yet?) Retrying in a few seconds." << incoming->errorString();
|
||||
QTimer::singleShot(10000, this, &HttpCredentials::fetchFromKeychainHelper);
|
||||
_retryOnKeyChainError = false;
|
||||
return;
|
||||
}
|
||||
_retryOnKeyChainError = false;
|
||||
#endif
|
||||
|
||||
// Store PEM in memory
|
||||
ReadPasswordJob *readJob = static_cast<ReadPasswordJob *>(incoming);
|
||||
if (readJob->error() == NoError && readJob->binaryData().length() > 0) {
|
||||
|
||||
@@ -141,6 +141,7 @@ protected:
|
||||
QSslKey _clientSslKey;
|
||||
QSslCertificate _clientSslCertificate;
|
||||
bool _keychainMigration;
|
||||
bool _retryOnKeyChainError = true; // true if we haven't done yet any reading from keychain
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -579,8 +579,7 @@ bool OwncloudPropagator::localFileNameClash(const QString &relFile)
|
||||
re = false;
|
||||
qCWarning(lcPropagator) << "No valid fileinfo";
|
||||
} else {
|
||||
// Need to normalize to composited form because of
|
||||
// https://bugreports.qt-project.org/browse/QTBUG-39622
|
||||
// Need to normalize to composited form because of QTBUG-39622/QTBUG-55896
|
||||
const QString cName = fileInfo.canonicalFilePath().normalized(QString::NormalizationForm_C);
|
||||
bool equal = (file == cName);
|
||||
re = (!equal && !cName.endsWith(relFile, Qt::CaseSensitive));
|
||||
@@ -821,10 +820,13 @@ void PropagatorCompositeJob::slotSubJobFinished(SyncFileItem::Status status)
|
||||
ASSERT(i >= 0);
|
||||
_runningJobs.remove(i);
|
||||
|
||||
// Any sub job error will cause the whole composite to fail. This is important
|
||||
// for knowing whether to update the etag in PropagateDirectory, for example.
|
||||
if (status == SyncFileItem::FatalError
|
||||
|| status == SyncFileItem::NormalError
|
||||
|| status == SyncFileItem::SoftError
|
||||
|| status == SyncFileItem::DetailError) {
|
||||
|| status == SyncFileItem::DetailError
|
||||
|| status == SyncFileItem::BlacklistedError) {
|
||||
_hasError = status;
|
||||
}
|
||||
|
||||
|
||||
@@ -553,6 +553,16 @@ void PropagateUploadFileCommon::commonErrorHandling(AbstractNetworkJob *job)
|
||||
abortWithError(status, errorString);
|
||||
}
|
||||
|
||||
void PropagateUploadFileCommon::adjustLastJobTimeout(AbstractNetworkJob *job, quint64 fileSize)
|
||||
{
|
||||
job->setTimeout(qBound(
|
||||
job->timeoutMsec(),
|
||||
// Calculate 3 minutes for each gigabyte of data
|
||||
qint64((3 * 60 * 1000) * fileSize / 1e9),
|
||||
// Maximum of 30 minutes
|
||||
qint64(30 * 60 * 1000)));
|
||||
}
|
||||
|
||||
void PropagateUploadFileCommon::slotJobDestroyed(QObject *job)
|
||||
{
|
||||
_jobs.erase(std::remove(_jobs.begin(), _jobs.end(), job), _jobs.end());
|
||||
|
||||
@@ -281,6 +281,17 @@ protected:
|
||||
*/
|
||||
void commonErrorHandling(AbstractNetworkJob *job);
|
||||
|
||||
/**
|
||||
* Increases the timeout for the final MOVE/PUT for large files.
|
||||
*
|
||||
* This is an unfortunate workaround since the drawback is not being able to
|
||||
* detect real disconnects in a timely manner. Shall go away when the server
|
||||
* response starts coming quicker, or there is some sort of async api.
|
||||
*
|
||||
* See #6527, enterprise#2480
|
||||
*/
|
||||
static void adjustLastJobTimeout(AbstractNetworkJob *job, quint64 fileSize);
|
||||
|
||||
// Bases headers that need to be sent with every chunk
|
||||
QMap<QByteArray, QByteArray> headers();
|
||||
};
|
||||
|
||||
@@ -295,6 +295,7 @@ void PropagateUploadFileNG::startNextChunk()
|
||||
connect(job, &MoveJob::finishedSignal, this, &PropagateUploadFileNG::slotMoveJobFinished);
|
||||
connect(job, &QObject::destroyed, this, &PropagateUploadFileCommon::slotJobDestroyed);
|
||||
propagator()->_activeJobList.append(this);
|
||||
adjustLastJobTimeout(job, fileSize);
|
||||
job->start();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -130,6 +130,8 @@ void PropagateUploadFileV1::startNextChunk()
|
||||
connect(job, &PUTFileJob::uploadProgress, this, &PropagateUploadFileV1::slotUploadProgress);
|
||||
connect(job, &PUTFileJob::uploadProgress, device, &UploadDevice::slotJobUploadProgress);
|
||||
connect(job, &QObject::destroyed, this, &PropagateUploadFileCommon::slotJobDestroyed);
|
||||
if (isFinalChunk)
|
||||
adjustLastJobTimeout(job, fileSize);
|
||||
job->start();
|
||||
propagator()->_activeJobList.append(this);
|
||||
_currentChunk++;
|
||||
@@ -301,6 +303,7 @@ void PropagateUploadFileV1::slotPutFinished()
|
||||
qCWarning(lcPropagateUpload) << "Server does not support X-OC-MTime" << job->reply()->rawHeader("X-OC-MTime");
|
||||
// Well, the mtime was not set
|
||||
done(SyncFileItem::SoftError, "Server does not support X-OC-MTime");
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef WITH_TESTING
|
||||
|
||||
@@ -82,10 +82,9 @@ public:
|
||||
/** For files whose errors were blacklisted
|
||||
*
|
||||
* If an file is blacklisted due to an error it isn't even reattempted. These
|
||||
* errors should appear in the issues tab, but not on the account settings and
|
||||
* should not cause the sync run to fail.
|
||||
* errors should appear in the issues tab but should be silent otherwise.
|
||||
*
|
||||
* A DetailError that doesn't cause sync failure.
|
||||
* A SoftError caused by blacklisting.
|
||||
*/
|
||||
BlacklistedError
|
||||
};
|
||||
|
||||
@@ -576,6 +576,51 @@ private slots:
|
||||
//QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
|
||||
}
|
||||
}
|
||||
|
||||
// https://github.com/owncloud/client/issues/6629#issuecomment-402450691
|
||||
// When a file is moved and the server mtime was not in sync, the local mtime should be kept
|
||||
void testMoveAndMTimeChange()
|
||||
{
|
||||
FakeFolder fakeFolder{ FileInfo::A12_B12_C12_S12() };
|
||||
int nPUT = 0;
|
||||
int nDELETE = 0;
|
||||
int nGET = 0;
|
||||
int nMOVE = 0;
|
||||
fakeFolder.setServerOverride([&](QNetworkAccessManager::Operation op, const QNetworkRequest &req) {
|
||||
if (op == QNetworkAccessManager::PutOperation)
|
||||
++nPUT;
|
||||
if (op == QNetworkAccessManager::DeleteOperation)
|
||||
++nDELETE;
|
||||
if (op == QNetworkAccessManager::GetOperation)
|
||||
++nGET;
|
||||
if (req.attribute(QNetworkRequest::CustomVerbAttribute) == "MOVE")
|
||||
++nMOVE;
|
||||
return nullptr;
|
||||
});
|
||||
|
||||
// Changing the mtime on the server (without invalidating the etag)
|
||||
fakeFolder.remoteModifier().find("A/a1")->lastModified = QDateTime::currentDateTimeUtc().addSecs(-50000);
|
||||
fakeFolder.remoteModifier().find("A/a2")->lastModified = QDateTime::currentDateTimeUtc().addSecs(-40000);
|
||||
|
||||
// Move a few files
|
||||
fakeFolder.remoteModifier().rename("A/a1", "A/a1_server_renamed");
|
||||
fakeFolder.localModifier().rename("A/a2", "A/a2_local_renamed");
|
||||
|
||||
QVERIFY(fakeFolder.syncOnce());
|
||||
QCOMPARE(nGET, 0);
|
||||
QCOMPARE(nPUT, 0);
|
||||
QCOMPARE(nMOVE, 1);
|
||||
QCOMPARE(nDELETE, 0);
|
||||
|
||||
// Another sync should do nothing
|
||||
QVERIFY(fakeFolder.syncOnce());
|
||||
QCOMPARE(nGET, 0);
|
||||
QCOMPARE(nPUT, 0);
|
||||
QCOMPARE(nMOVE, 1);
|
||||
QCOMPARE(nDELETE, 0);
|
||||
|
||||
QCOMPARE(fakeFolder.currentLocalState(), fakeFolder.currentRemoteState());
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(TestSyncMove)
|
||||
|
||||
@@ -373,7 +373,7 @@
|
||||
<message>
|
||||
<location filename="../src/gui/accountstate.cpp" line="132"/>
|
||||
<source>Maintenance mode</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Mode de manteniment</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/accountstate.cpp" line="134"/>
|
||||
@@ -1415,12 +1415,12 @@ Els elements que poden ser eliminats s'eliminaran si impedeixen que una car
|
||||
<message>
|
||||
<location filename="../src/gui/issueswidget.ui" line="73"/>
|
||||
<source>Show warnings</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Mostra avisos</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/issueswidget.ui" line="83"/>
|
||||
<source>Show ignored files</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Mostra arxius descartats</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/issueswidget.ui" line="133"/>
|
||||
@@ -2507,7 +2507,7 @@ No és aconsellada usar-la.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/servernotificationhandler.cpp" line="103"/>
|
||||
<source>Dismiss</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Ignora</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -2622,12 +2622,12 @@ No és aconsellada usar-la.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/sharedialog.cpp" line="211"/>
|
||||
<source>Users and Groups</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Usuaris i Grups</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/sharedialog.cpp" line="218"/>
|
||||
<source>Public Links</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Enllaços públics</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -2650,7 +2650,7 @@ No és aconsellada usar-la.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.ui" line="35"/>
|
||||
<source>Enter a name to create a new public link...</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Escriu un nom per crear un enllaç públic...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.ui" line="42"/>
|
||||
@@ -2670,12 +2670,12 @@ No és aconsellada usar-la.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.ui" line="178"/>
|
||||
<source>Link properties:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Propietats de l'enllaç:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.ui" line="220"/>
|
||||
<source>Show file listing</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Motra el llistat de fitxers</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.ui" line="200"/>
|
||||
@@ -2685,7 +2685,7 @@ No és aconsellada usar-la.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.ui" line="20"/>
|
||||
<source>Anyone with the link has access to the file/folder</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Qualsevol podrà accedir a l'arxiu o carpeta amb aquest link</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.cpp" line="117"/>
|
||||
@@ -2727,7 +2727,7 @@ No és aconsellada usar-la.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.cpp" line="164"/>
|
||||
<source>Copy link to clipboard</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Copiar al portadocuments</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.cpp" line="165"/>
|
||||
@@ -2763,7 +2763,7 @@ No és aconsellada usar-la.</translation>
|
||||
<location filename="../src/gui/sharelinkwidget.cpp" line="63"/>
|
||||
<location filename="../src/gui/sharelinkwidget.cpp" line="543"/>
|
||||
<source>Public link</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Enllaç públic</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.cpp" line="253"/>
|
||||
@@ -2811,7 +2811,7 @@ No és aconsellada usar-la.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/shareusergroupwidget.cpp" line="237"/>
|
||||
<source>Copy link to clipboard</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Copiar al portadocuments</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/shareusergroupwidget.cpp" line="239"/>
|
||||
@@ -2826,7 +2826,7 @@ No és aconsellada usar-la.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/shareusergroupwidget.cpp" line="337"/>
|
||||
<source>I shared something with you</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>He compartit amb tu</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -2919,7 +2919,7 @@ No és aconsellada usar-la.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/socketapi.cpp" line="557"/>
|
||||
<source>I shared something with you</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>He compartit amb tu</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/socketapi.cpp" line="565"/>
|
||||
@@ -3850,7 +3850,7 @@ No és aconsellada usar-la.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/wizard/owncloudoauthcredspage.ui" line="56"/>
|
||||
<source>Re-open Browser</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Reobrir navegador</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -4177,7 +4177,7 @@ No és aconsellada usar-la.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/guiutility.cpp" line="33"/>
|
||||
<source>Could not open browser</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>No es pot obrir el navegador</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/guiutility.cpp" line="34"/>
|
||||
|
||||
@@ -83,12 +83,12 @@
|
||||
<message>
|
||||
<location filename="../src/libsync/abstractnetworkjob.cpp" line="247"/>
|
||||
<source>Unknown error: network reply was deleted</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Neznámá chyba: odpověď sítě byla smazána</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/abstractnetworkjob.cpp" line="390"/>
|
||||
<source>Server replied "%1 %2" to "%3 %4"</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Odpověď serveru "%1 %2" do "%3 %4"</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -388,7 +388,7 @@
|
||||
<message>
|
||||
<location filename="../src/gui/accountstate.cpp" line="138"/>
|
||||
<source>Asking Credentials</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Požádat o pověření</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/accountstate.cpp" line="140"/>
|
||||
@@ -988,7 +988,7 @@ Pokračováním v synchronizaci způsobí přepsání všech vašich souborů st
|
||||
<message>
|
||||
<location filename="../src/gui/folderstatusmodel.cpp" line="209"/>
|
||||
<source>There are unresolved conflicts. Click for details.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Jsou zde nevyřešené konflikty. Klikněte pro detaily.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/folderstatusmodel.cpp" line="878"/>
|
||||
@@ -1396,7 +1396,7 @@ Položky u kterých je povoleno smazání budou vymazány, pokud by bránily ods
|
||||
<message>
|
||||
<location filename="../src/gui/issueswidget.ui" line="20"/>
|
||||
<source>List of issues</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Seznam problémů</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/issueswidget.ui" line="34"/>
|
||||
@@ -3628,12 +3628,12 @@ Nedoporučuje se jí používat.</translation>
|
||||
<location filename="../src/gui/owncloudgui.cpp" line="273"/>
|
||||
<location filename="../src/gui/owncloudgui.cpp" line="304"/>
|
||||
<source>Synchronization is paused</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Synchronizace je pozastavena</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudgui.cpp" line="306"/>
|
||||
<source>Error during synchronization</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Chyba při synchronizaci</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudgui.cpp" line="314"/>
|
||||
@@ -3843,17 +3843,17 @@ Nedoporučuje se jí používat.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/wizard/owncloudoauthcredspage.ui" line="36"/>
|
||||
<source>Please switch to your browser to proceed.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Chcete-li pokračovat, přepněte prosím do svého prohlížeče.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/wizard/owncloudoauthcredspage.ui" line="46"/>
|
||||
<source>An error occured while connecting. Please try again.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Při připojování došlo k chybě. Prosím zkuste to znovu.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/wizard/owncloudoauthcredspage.ui" line="56"/>
|
||||
<source>Re-open Browser</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Znovu otevřít prohlížeč</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -4180,7 +4180,7 @@ Nedoporučuje se jí používat.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/guiutility.cpp" line="33"/>
|
||||
<source>Could not open browser</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Nemohu otevřít prohlížeč</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/guiutility.cpp" line="34"/>
|
||||
|
||||
@@ -60,12 +60,12 @@
|
||||
<message>
|
||||
<location filename="../src/gui/notificationwidget.ui" line="56"/>
|
||||
<source>Lorem ipsum dolor sit amet</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Lorem ipsum dolor sit amet</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/notificationwidget.ui" line="69"/>
|
||||
<source>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod temporm </source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod temporm</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/notificationwidget.ui" line="89"/>
|
||||
@@ -83,7 +83,7 @@
|
||||
<message>
|
||||
<location filename="../src/libsync/abstractnetworkjob.cpp" line="247"/>
|
||||
<source>Unknown error: network reply was deleted</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Error desconocido: la respuesta de la red fue eliminada</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/abstractnetworkjob.cpp" line="390"/>
|
||||
|
||||
@@ -83,12 +83,12 @@
|
||||
<message>
|
||||
<location filename="../src/libsync/abstractnetworkjob.cpp" line="247"/>
|
||||
<source>Unknown error: network reply was deleted</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>未知のエラーです:ネットワーク応答が削除されました</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/abstractnetworkjob.cpp" line="390"/>
|
||||
<source>Server replied "%1 %2" to "%3 %4"</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>サーバの返答は "%1 %2" から "%3 %4"です</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -253,7 +253,7 @@
|
||||
<message>
|
||||
<location filename="../src/gui/accountsettings.cpp" line="667"/>
|
||||
<source>Obtaining authorization from the browser. <a href='%1'>Click here</a> to re-open the browser.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>ブラウザから権限を取得しています。<a href='%1'>ここをクリック</a>してブラウザを再度開いてください。 </translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/accountsettings.cpp" line="671"/>
|
||||
@@ -1428,12 +1428,12 @@ Items where deletion is allowed will be deleted if they prevent a directory from
|
||||
<message>
|
||||
<location filename="../src/gui/issueswidget.ui" line="133"/>
|
||||
<source>There were too many issues. Not all will be visible here.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>問題が多すぎます。すべてはここに表示できません。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/issueswidget.ui" line="155"/>
|
||||
<source>Copy the issues list to the clipboard.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>問題リストをクリップボードにコピーしてください。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/issueswidget.ui" line="158"/>
|
||||
@@ -1693,7 +1693,7 @@ Items where deletion is allowed will be deleted if they prevent a directory from
|
||||
<message>
|
||||
<location filename="../src/gui/creds/oauth.cpp" line="112"/>
|
||||
<source>Error returned from the server: <em>%1</em></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>サーバからエラーが返されました: <em>%1</em> </translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/creds/oauth.cpp" line="115"/>
|
||||
@@ -1708,17 +1708,17 @@ Items where deletion is allowed will be deleted if they prevent a directory from
|
||||
<message>
|
||||
<location filename="../src/gui/creds/oauth.cpp" line="121"/>
|
||||
<source>The reply from the server did not contain all expected fields</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>サーバからの返答にあるべきフィールドがありません</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/creds/oauth.cpp" line="125"/>
|
||||
<source><h1>Login Error</h1><p>%1</p></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation><h1>ログインエラー</h1><p>%1</p> </translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/creds/oauth.cpp" line="131"/>
|
||||
<source><h1>Wrong user</h1><p>You logged-in with user <em>%1</em>, but must login with user <em>%2</em>.<br>Please log out of %3 in another tab, then <a href='%4'>click here</a> and log in as user %2</p></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation><h1>ユーザの誤りです</h1><p><em>%1</em>としてログインされましたがm、<em>%2</em>でなければいけません。<br>別のタブで %3 からログアウトし、<a href='%4'>ここをクリック</a>して %2 としてログインしてください</p> </translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -1885,7 +1885,7 @@ for additional privileges during the process.</source>
|
||||
<message>
|
||||
<location filename="../src/gui/wizard/owncloudoauthcredspage.cpp" line="44"/>
|
||||
<source>Login in your browser</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>御自身のブラウザでログインしてください</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -1962,7 +1962,7 @@ It is not advisable to use it.</source>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="297"/>
|
||||
<source>The server reported the following error:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>サーバが以下のエラーを報告しています:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="452"/>
|
||||
@@ -2723,12 +2723,12 @@ It is not advisable to use it.</source>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.cpp" line="163"/>
|
||||
<source>Open link in browser</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>ブラウザでリンクを開く</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.cpp" line="164"/>
|
||||
<source>Copy link to clipboard</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>クリップボードにリンクをコピー</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.cpp" line="165"/>
|
||||
@@ -2807,12 +2807,12 @@ It is not advisable to use it.</source>
|
||||
<message>
|
||||
<location filename="../src/gui/shareusergroupwidget.cpp" line="235"/>
|
||||
<source>Open link in browser</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>ブラウザでリンクを開く</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/shareusergroupwidget.cpp" line="237"/>
|
||||
<source>Copy link to clipboard</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>クリップボードにリンクをコピー</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/shareusergroupwidget.cpp" line="239"/>
|
||||
@@ -4178,12 +4178,12 @@ It is not advisable to use it.</source>
|
||||
<message>
|
||||
<location filename="../src/gui/guiutility.cpp" line="33"/>
|
||||
<source>Could not open browser</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>ブラウザを開くことができませんでした。</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/guiutility.cpp" line="34"/>
|
||||
<source>There was an error when launching the browser to go to URL %1. Maybe no default browser is configured?</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>URL %1 をブラウザで開く際にエラーが発生しました。おそらくデフォルトのブラウザが設定されていないのでは?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/guiutility.cpp" line="55"/>
|
||||
|
||||
@@ -1720,7 +1720,7 @@ Elementer hvor sletting er tillatt, vil bli slettet hvis de forhindrer fjerning
|
||||
<message>
|
||||
<location filename="../src/gui/creds/oauth.cpp" line="131"/>
|
||||
<source><h1>Wrong user</h1><p>You logged-in with user <em>%1</em>, but must login with user <em>%2</em>.<br>Please log out of %3 in another tab, then <a href='%4'>click here</a> and log in as user %2</p></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation><h1>Feil bruker</h1><p>Du logget inn med bruker <em>%1</em>, men må logge inn med bruker <em>%2</em>.<br>Logg ut av %3 i en annen tab, <a href='%4'>klikk deretter her</a> og logg inn som bruker %2</p></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
||||
@@ -83,7 +83,7 @@
|
||||
<message>
|
||||
<location filename="../src/libsync/abstractnetworkjob.cpp" line="247"/>
|
||||
<source>Unknown error: network reply was deleted</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Onbekende fout: antwoord van het netwerk is verwijderd</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/abstractnetworkjob.cpp" line="390"/>
|
||||
@@ -243,7 +243,7 @@
|
||||
<message>
|
||||
<location filename="../src/gui/accountsettings.cpp" line="656"/>
|
||||
<source>Server %1 is currently in maintenance mode.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Server %1 is momenteel in onderhoudsmodus.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/accountsettings.cpp" line="658"/>
|
||||
@@ -253,7 +253,7 @@
|
||||
<message>
|
||||
<location filename="../src/gui/accountsettings.cpp" line="667"/>
|
||||
<source>Obtaining authorization from the browser. <a href='%1'>Click here</a> to re-open the browser.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Probeert autorisatie te krijgen van de browser. 1 Klik hier 1 om de browser opnieuw te openen.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/accountsettings.cpp" line="671"/>
|
||||
@@ -990,7 +990,7 @@ Doorgaan met deze synchronisatie overschrijft al uw bestanden door een eerdere v
|
||||
<message>
|
||||
<location filename="../src/gui/folderstatusmodel.cpp" line="209"/>
|
||||
<source>There are unresolved conflicts. Click for details.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Er zijn onopgeloste conflicten. Klik voor details.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/folderstatusmodel.cpp" line="878"/>
|
||||
@@ -1000,7 +1000,7 @@ Doorgaan met deze synchronisatie overschrijft al uw bestanden door een eerdere v
|
||||
<message>
|
||||
<location filename="../src/gui/folderstatusmodel.cpp" line="884"/>
|
||||
<source>Reconciling changes</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Wijzigingen doorvoeren</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/folderstatusmodel.cpp" line="919"/>
|
||||
@@ -1434,12 +1434,12 @@ Onderdelen die gewist mogen worden worden verwijderd als ze voorkomen dat een ma
|
||||
<message>
|
||||
<location filename="../src/gui/issueswidget.ui" line="133"/>
|
||||
<source>There were too many issues. Not all will be visible here.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Er zijn teveel problemen. Niet alles zal worden vertoond.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/issueswidget.ui" line="155"/>
|
||||
<source>Copy the issues list to the clipboard.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Kopieer de probleemlijst naar het klembord.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/issueswidget.ui" line="158"/>
|
||||
@@ -1482,7 +1482,7 @@ Onderdelen die gewist mogen worden worden verwijderd als ze voorkomen dat een ma
|
||||
<message>
|
||||
<location filename="../src/gui/logbrowser.cpp" line="89"/>
|
||||
<source>&Capture debug messages</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Capture berichten debuggen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/logbrowser.cpp" line="101"/>
|
||||
@@ -1704,17 +1704,17 @@ Onderdelen die gewist mogen worden worden verwijderd als ze voorkomen dat een ma
|
||||
<message>
|
||||
<location filename="../src/gui/creds/oauth.cpp" line="115"/>
|
||||
<source>There was an error accessing the 'token' endpoint: <br><em>%1</em></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Er heeft zich een fout voorgedaan bij het verkrijgen van de 'token' eindpunt: 1 2 %1 2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/creds/oauth.cpp" line="118"/>
|
||||
<source>Could not parse the JSON returned from the server: <br><em>%1</em></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Kon de JSON dat teruggekomen is van de server niet ontleden: 1 2 %1 2</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/creds/oauth.cpp" line="121"/>
|
||||
<source>The reply from the server did not contain all expected fields</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Het antwoord van de server bevat niet alle verwachte velden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/creds/oauth.cpp" line="125"/>
|
||||
@@ -1724,7 +1724,7 @@ Onderdelen die gewist mogen worden worden verwijderd als ze voorkomen dat een ma
|
||||
<message>
|
||||
<location filename="../src/gui/creds/oauth.cpp" line="131"/>
|
||||
<source><h1>Wrong user</h1><p>You logged-in with user <em>%1</em>, but must login with user <em>%2</em>.<br>Please log out of %3 in another tab, then <a href='%4'>click here</a> and log in as user %2</p></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>1 Verkeerde gebruiker 1 2 U bent ingelogd met user 3 %1 3, maar dient ingelogd te zijn met user 4 %2 4. 5 Log %3 alstublieft uit in een andere tabblad, vervolgens 6 klikt u hier 6 en log-in als user %2 2</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -1892,7 +1892,7 @@ vragen om extra autorisaties tijdens installatie.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/wizard/owncloudoauthcredspage.cpp" line="44"/>
|
||||
<source>Login in your browser</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Login in uw browser</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -1970,7 +1970,7 @@ We adviseren deze site niet te gebruiken.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="297"/>
|
||||
<source>The server reported the following error:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>De server rapporteerde de volgende fout:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="452"/>
|
||||
@@ -2132,7 +2132,7 @@ We adviseren deze site niet te gebruiken.</translation>
|
||||
<message>
|
||||
<location filename="../src/libsync/propagatedownload.cpp" line="459"/>
|
||||
<source>The download would reduce free local disk space below the limit</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>De download zal de vrije lokale schijfruimte reduceren tot onder het limiet</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/propagatedownload.cpp" line="463"/>
|
||||
@@ -2319,7 +2319,7 @@ We adviseren deze site niet te gebruiken.</translation>
|
||||
<location filename="../src/libsync/propagateupload.cpp" line="186"/>
|
||||
<location filename="../src/libsync/propagateupload.cpp" line="549"/>
|
||||
<source>Upload of %1 exceeds the quota for the folder</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Upload van %1 overtreft de quota van de folder</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/propagateupload.cpp" line="622"/>
|
||||
@@ -2659,12 +2659,12 @@ We adviseren deze site niet te gebruiken.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.ui" line="35"/>
|
||||
<source>Enter a name to create a new public link...</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Voer een naam in om een nieuwe publieke link aan te maken</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.ui" line="42"/>
|
||||
<source>&Create new</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>&Create nieuw</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.ui" line="105"/>
|
||||
@@ -2715,12 +2715,12 @@ We adviseren deze site niet te gebruiken.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.cpp" line="93"/>
|
||||
<source>Link shares have been disabled</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Link delen zijn uitgeschakeld</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.cpp" line="107"/>
|
||||
<source>Create public link share</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Maak een verdeelbare publieke link aan</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.cpp" line="168"/>
|
||||
@@ -2741,7 +2741,7 @@ We adviseren deze site niet te gebruiken.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.cpp" line="165"/>
|
||||
<source>Copy link to clipboard (direct download)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Kopieer de link naar het klembord (directe download)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.cpp" line="166"/>
|
||||
@@ -2751,17 +2751,17 @@ We adviseren deze site niet te gebruiken.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.cpp" line="167"/>
|
||||
<source>Send link by email (direct download)</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Verzend link met email (directe download)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.cpp" line="519"/>
|
||||
<source>Confirm Link Share Deletion</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Bevestig Gedeelde Link Verwijdering</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.cpp" line="520"/>
|
||||
<source><p>Do you really want to delete the public link share <i>%1</i>?</p><p>Note: This action cannot be undone.</p></source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>1 Wilt u echt de verdeelbare publieke link verwijderen 2 %1 2 ? 1 3 Let op: deze actie kan niet ongedaan gemaakt worden. 3</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.cpp" line="527"/>
|
||||
@@ -2777,7 +2777,7 @@ We adviseren deze site niet te gebruiken.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.cpp" line="253"/>
|
||||
<source>Delete link share</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Verwijder verdeelbare link</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.cpp" line="458"/>
|
||||
@@ -4186,7 +4186,7 @@ We adviseren deze site niet te gebruiken.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/guiutility.cpp" line="33"/>
|
||||
<source>Could not open browser</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Kon het browser niet openen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/guiutility.cpp" line="34"/>
|
||||
|
||||
@@ -1966,7 +1966,7 @@ Não é aconselhada a sua utilização.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="297"/>
|
||||
<source>The server reported the following error:</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>O servidor retornou o seguinte erro:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/owncloudsetupwizard.cpp" line="452"/>
|
||||
@@ -2727,7 +2727,7 @@ Não é aconselhada a sua utilização.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.cpp" line="163"/>
|
||||
<source>Open link in browser</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Abrir ligação no navegador</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.cpp" line="164"/>
|
||||
@@ -2742,7 +2742,7 @@ Não é aconselhada a sua utilização.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.cpp" line="166"/>
|
||||
<source>Send link by email</source>
|
||||
<translation>Enviar hiperligação por e-mail</translation>
|
||||
<translation>Enviar hiperligação por correio eletrónico</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.cpp" line="167"/>
|
||||
@@ -2811,7 +2811,7 @@ Não é aconselhada a sua utilização.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/shareusergroupwidget.cpp" line="235"/>
|
||||
<source>Open link in browser</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Abrir ligação no navegador</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/shareusergroupwidget.cpp" line="237"/>
|
||||
@@ -2821,7 +2821,7 @@ Não é aconselhada a sua utilização.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/shareusergroupwidget.cpp" line="239"/>
|
||||
<source>Send link by email</source>
|
||||
<translation>Enviar hiperligação por e-mail</translation>
|
||||
<translation>Enviar hiperligação por correio eletrónico</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/shareusergroupwidget.cpp" line="249"/>
|
||||
@@ -2939,7 +2939,7 @@ Não é aconselhada a sua utilização.</translation>
|
||||
<message>
|
||||
<location filename="../src/gui/socketapi.cpp" line="568"/>
|
||||
<source>Send private link by email...</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Enviar hiperligação privada por correio eletrónico...</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
||||
@@ -83,12 +83,12 @@
|
||||
<message>
|
||||
<location filename="../src/libsync/abstractnetworkjob.cpp" line="247"/>
|
||||
<source>Unknown error: network reply was deleted</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Незнайома помилка: відповідь мережі була видалена</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/libsync/abstractnetworkjob.cpp" line="390"/>
|
||||
<source>Server replied "%1 %2" to "%3 %4"</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Сервер відповів "%1 %2" на "%3 %4"</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -228,7 +228,7 @@
|
||||
<message>
|
||||
<location filename="../src/gui/accountsettings.cpp" line="650"/>
|
||||
<source>The server version %1 is old and unsupported! Proceed at your own risk.</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Версія на вашому сервері %1 застаріла та вже не підтримується! Продовжуйте на свій ризик та розсуд.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/accountsettings.cpp" line="652"/>
|
||||
@@ -258,7 +258,7 @@
|
||||
<message>
|
||||
<location filename="../src/gui/accountsettings.cpp" line="671"/>
|
||||
<source>Connecting to %1...</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>З'єднання з %1...</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/accountsettings.cpp" line="674"/>
|
||||
@@ -567,7 +567,7 @@
|
||||
<message>
|
||||
<location filename="../src/gui/application.cpp" line="168"/>
|
||||
<source>Quit ownCloud</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Вийти з ownCloud</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
@@ -1387,7 +1387,7 @@ Items where deletion is allowed will be deleted if they prevent a directory from
|
||||
<message>
|
||||
<location filename="../src/gui/issueswidget.ui" line="20"/>
|
||||
<source>List of issues</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>Перелік проблем</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/issueswidget.ui" line="34"/>
|
||||
|
||||
@@ -2671,7 +2671,7 @@ It is not advisable to use it.</source>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.ui" line="220"/>
|
||||
<source>Show file listing</source>
|
||||
<translation type="unfinished"/>
|
||||
<translation>顯示檔案列表</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/gui/sharelinkwidget.ui" line="200"/>
|
||||
|
||||
Reference in New Issue
Block a user