Comparar commits

...

9 Commits

Autor SHA1 Mensagem Data
Klaas Freitag e95b73dfac Avatar: Set a circle mask around the avatar image.
The server displays the avatar cut into a circle, and so we do.
2017-01-23 21:36:18 +01:00
Klaas Freitag c00e3e8c0a ConnectionValidator: Set a 20 second timeout for Avatar request. 2017-01-23 21:35:12 +01:00
Klaas Freitag 2a12610a46 Update ASCII-Art describing the connection check flow.
Added Avatar fetching
2017-01-23 21:34:34 +01:00
Klaas Freitag d466a05915 SettingsDialog: Display the user avatar as action icon if available.
The avatar image is fetched from the server async, thus connect a signal
from the account if the avatar changes.

Server feature https://github.com/owncloud/core/pull/26872 is needed.
2017-01-22 13:58:36 +01:00
Klaas Freitag e05d6bfcdc Make the Account store the avatar pixmap.
The avatar pixmap is fetched from the server by the Connectionvalidator,
once it has validated the user name, it queries the avatar pixmap.

If the server does not have the avatar route, an empty pixmap is stored.
2017-01-22 13:55:08 +01:00
Klaas Freitag b49dd02e3d New network job AvatarJob: GETs the account avatar from server. 2017-01-22 13:52:19 +01:00
Olivier Goffart d6fdda8efa ChunkingNG: add '0' padding on the filename (#5476)
The server sorts the chunk by name alphabetically. So if we want to keep
the chunk in order, we need to add a few '0' in front of the chunk name
2017-01-20 16:03:50 +01:00
Markus Goetz faedaa5e09 Update issue_template.md 2017-01-20 16:00:50 +01:00
Jenkins for ownCloud 661c7f0558 [tx-robot] updated from transifex 2017-01-20 02:18:34 +01:00
18 arquivos alterados com 207 adições e 32 exclusões
+4
Ver Arquivo
@@ -37,6 +37,10 @@ Operating system:
OS language:
Qt version used by client package (Linux only, see also Settings dialog):
Client package (From ownCloud or distro) (Linux only):
Installation path of client:
### Logs
+3
Ver Arquivo
@@ -643,6 +643,9 @@ X-GNOME-Autostart-Delay=3
# Translations
# Translations
# Translations
Comment[oc]=@APPLICATION_NAME@ sincronizacion del client
GenericName[oc]=Dorsièr de Sincronizacion
+61 -6
Ver Arquivo
@@ -40,6 +40,8 @@
#include <QPixmap>
#include <QImage>
#include <QWidgetAction>
#include <QPainter>
#include <QPainterPath>
namespace {
const char TOOLBAR_CSS[] =
@@ -54,6 +56,23 @@ namespace {
namespace OCC {
static QIcon circleMask( const QPixmap& avatar )
{
int dim = avatar.width();
QPixmap fixedImage(dim, dim);
fixedImage.fill(Qt::transparent);
QPainter imgPainter(&fixedImage);
QPainterPath clip;
clip.addEllipse(0, 0, dim, dim);
imgPainter.setClipPath(clip);
imgPainter.drawPixmap(0, 0, dim, dim, avatar);
imgPainter.end();
return QIcon(fixedImage);
}
//
// Whenever you change something here check both settingsdialog.cpp and settingsdialogmac.cpp !
//
@@ -196,8 +215,17 @@ void SettingsDialog::accountAdded(AccountState *s)
bool brandingSingleAccount = !Theme::instance()->multiAccount();
auto accountAction = createColorAwareAction(QLatin1String(":/client/resources/account.png"),
brandingSingleAccount ? tr("Account") : s->account()->displayName());
QAction *accountAction;
QPixmap avatar = s->account()->avatar();
const QString actionText = brandingSingleAccount ? tr("Account") : s->account()->displayName();
if(avatar.isNull()) {
accountAction = createColorAwareAction(QLatin1String(":/client/resources/account.png"),
actionText);
} else {
QIcon icon = circleMask(avatar);
accountAction = createActionWithIcon(icon, actionText);
}
if (!brandingSingleAccount) {
accountAction->setToolTip(s->account()->displayName());
accountAction->setIconText(s->shortDisplayNameForSettings(height * buttonSizeRatio));
@@ -207,14 +235,30 @@ void SettingsDialog::accountAdded(AccountState *s)
_ui->stack->insertWidget(0 , accountSettings);
_actionGroup->addAction(accountAction);
_actionGroupWidgets.insert(accountAction, accountSettings);
_actionForAccount.insert(s->account().data(), accountAction);
connect( accountSettings, SIGNAL(folderChanged()), _gui, SLOT(slotFoldersChanged()));
connect( accountSettings, SIGNAL(openFolderAlias(const QString&)),
_gui, SLOT(slotFolderOpenAction(QString)));
connect(s->account().data(), SIGNAL(accountChangedAvatar()), SLOT(slotAccountAvatarChanged()));
slotRefreshActivity(s);
}
void SettingsDialog::slotAccountAvatarChanged()
{
Account *account = static_cast<Account*>(sender());
if( account && _actionForAccount.contains(account)) {
QAction *action = _actionForAccount[account];
if( action ) {
QPixmap pix = account->avatar();
if( !pix.isNull() ) {
action->setIcon( circleMask(pix) );
}
}
}
}
void SettingsDialog::accountRemoved(AccountState *s)
{
for (auto it = _actionGroupWidgets.begin(); it != _actionGroupWidgets.end(); ++it) {
@@ -236,6 +280,9 @@ void SettingsDialog::accountRemoved(AccountState *s)
}
}
if( _actionForAccount.contains(s->account().data()) ) {
_actionForAccount.remove(s->account().data());
}
_activitySettings->slotRemoveAccount(s);
// Hide when the last account is deleted. We want to enter the same
@@ -306,14 +353,22 @@ public:
}
};
QAction *SettingsDialog::createActionWithIcon(const QIcon& icon, const QString& text, const QString& iconPath)
{
QAction *action = new ToolButtonAction(icon, text, this);
action->setCheckable(true);
if(!iconPath.isEmpty()) {
action->setProperty("iconPath", iconPath);
}
return action;
}
QAction *SettingsDialog::createColorAwareAction(const QString &iconPath, const QString &text)
{
// all buttons must have the same size in order to keep a good layout
QIcon coloredIcon = createColorAwareIcon(iconPath);
QAction *action = new ToolButtonAction(coloredIcon, text, this);
action->setCheckable(true);
action->setProperty("iconPath", iconPath);
return action;
return createActionWithIcon(coloredIcon, text, iconPath);
}
void SettingsDialog::slotRefreshActivity( AccountState* accountState )
+7
Ver Arquivo
@@ -58,6 +58,7 @@ public slots:
void showActivityPage();
void slotSwitchPage(QAction *action);
void slotRefreshActivity(AccountState *accountState );
void slotAccountAvatarChanged();
protected:
void reject() Q_DECL_OVERRIDE;
@@ -73,12 +74,18 @@ private:
QIcon createColorAwareIcon(const QString &name);
QAction *createColorAwareAction(const QString &iconName, const QString &fileName);
QAction *createActionWithIcon(const QIcon& icon, const QString& text, const QString& iconPath = QString());
Ui::SettingsDialog * const _ui;
QActionGroup* _actionGroup;
// Maps the actions from the action group to the corresponding widgets
QHash<QAction*, QWidget*> _actionGroupWidgets;
// Maps the action in the dialog to their according account. Needed in
// case the account avatar changes
QHash<Account*, QAction*> _actionForAccount;
QToolBar* _toolBar;
ActivitySettings *_activitySettings;
+10
Ver Arquivo
@@ -90,6 +90,16 @@ void Account::setDavUser(const QString &newDavUser)
_davUser = newDavUser;
}
QPixmap Account::avatar() const
{
return _avatarPixmap;
}
void Account::setAvatar(const QPixmap& pixmap)
{
_avatarPixmap = pixmap;
emit accountChangedAvatar();
}
QString Account::displayName() const
{
QString dn = QString("%1@%2").arg(_credentials->user(), _url.host());
+8
Ver Arquivo
@@ -26,6 +26,8 @@
#include <QSslCipher>
#include <QSslError>
#include <QSharedPointer>
#include <QPixmap>
#include "utility.h"
#include <memory>
#include "capabilities.h"
@@ -78,6 +80,9 @@ public:
QString davUser() const;
void setDavUser(const QString &newDavUser);
QPixmap avatar() const;
void setAvatar(const QPixmap& pixmap);
/// The name of the account as shown in the toolbar
QString displayName() const;
@@ -197,6 +202,8 @@ signals:
void serverVersionChanged(Account* account, const QString& newVersion, const QString& oldVersion);
void accountChangedAvatar();
protected Q_SLOTS:
void slotHandleSslErrors(QNetworkReply*,QList<QSslError>);
void slotCredentialsFetched();
@@ -209,6 +216,7 @@ private:
QWeakPointer<Account> _sharedThis;
QString _id;
QString _davUser;
QPixmap _avatarPixmap;
QMap<QString, QVariant> _settingsMap;
QUrl _url;
QList<QSslCertificate> _approvedCerts;
+12
Ver Arquivo
@@ -15,6 +15,7 @@
#include <QtCore>
#include <QNetworkReply>
#include <QNetworkProxyFactory>
#include <QPixmap>
#include "connectionvalidator.h"
#include "account.h"
@@ -252,7 +253,18 @@ void ConnectionValidator::slotUserFetched(const QVariantMap &json)
QString user = json.value("ocs").toMap().value("data").toMap().value("id").toString();
if (!user.isEmpty()) {
_account->setDavUser(user);
AvatarJob *job = new AvatarJob(_account, this);
job->setTimeout(20*1000);
QObject::connect(job, SIGNAL(avatarPixmap(QPixmap)), this, SLOT(slotAvatarPixmap(QPixmap)));
job->start();
}
}
void ConnectionValidator::slotAvatarPixmap(const QPixmap& pixmap)
{
_account->setAvatar(pixmap);
reportResult(Connected);
}
+5 -1
Ver Arquivo
@@ -68,7 +68,10 @@ namespace OCC {
+-> fetchUser
PropfindJob
|
+-> slotUserFetched --> X
+-> slotUserFetched
AvatarJob
|
+-> slotAvatarPixmap --> reportResult()
\endcode
*/
@@ -119,6 +122,7 @@ protected slots:
void slotCapabilitiesRecieved(const QVariantMap&);
void slotUserFetched(const QVariantMap &);
void slotAvatarPixmap(const QPixmap&);
private:
void reportResult(Status status);
+37
Ver Arquivo
@@ -27,6 +27,7 @@
#include <QMutex>
#include <QDebug>
#include <QCoreApplication>
#include <QPixmap>
#include "json.h"
@@ -589,6 +590,42 @@ bool PropfindJob::finished()
/*********************************************************************************************/
AvatarJob::AvatarJob(AccountPtr account, QObject *parent)
: AbstractNetworkJob(account, QString(), parent)
{
_avatarUrl = Utility::concatUrlPath(account->url(), QString("remote.php/dav/avatars/%1/128.png").arg(account->davUser()));
}
void AvatarJob::start()
{
QNetworkRequest req;
setReply(davRequest("GET", _avatarUrl, req));
setupConnections(reply());
AbstractNetworkJob::start();
}
bool AvatarJob::finished()
{
int http_result_code = reply()->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QPixmap avPixmap;
if (http_result_code == 200) {
QByteArray pngData = reply()->readAll();
if( pngData.size() ) {
if( avPixmap.loadFromData(pngData) ) {
qDebug() << "Retrieved Avatar pixmap!";
}
}
}
emit(avatarPixmap(avPixmap));
return true;
}
/*********************************************************************************************/
ProppatchJob::ProppatchJob(AccountPtr account, const QString &path, QObject *parent)
: AbstractNetworkJob(account, path, parent)
{
+30
Ver Arquivo
@@ -129,6 +129,36 @@ private:
QList<QByteArray> _properties;
};
/**
* @brief The AvatarJob class
*
* Retrieves the account users avatar from the server using a GET request.
*
* If the server does not have the avatar, the result Pixmap is empty.
*
* @ingroup libsync
*/
class OWNCLOUDSYNC_EXPORT AvatarJob : public AbstractNetworkJob {
Q_OBJECT
public:
explicit AvatarJob(AccountPtr account, QObject *parent = 0);
void start() Q_DECL_OVERRIDE;
signals:
/**
* @brief avatarPixmap - returns either a valid pixmap or not.
*/
void avatarPixmap(QPixmap);
private slots:
virtual bool finished() Q_DECL_OVERRIDE;
private:
QUrl _avatarUrl;
};
/**
* @brief Send a Proppatch request
*
+2 -1
Ver Arquivo
@@ -300,7 +300,8 @@ private:
// Map chunk number with its size from the PROPFIND on resume.
// (Only used from slotPropfindIterate/slotPropfindFinished because the LsColJob use signals to report data.)
QMap<int, quint64> _serverChunks;
struct ServerChunkInfo { quint64 size; QString originalName; };
QMap<int, ServerChunkInfo> _serverChunks;
quint64 chunkSize() const { return propagator()->chunkSize(); }
/**
+9 -6
Ver Arquivo
@@ -41,7 +41,8 @@ QUrl PropagateUploadFileNG::chunkUrl(int chunk)
+ propagator()->account()->davUser()
+ QLatin1Char('/') + QString::number(_transferId);
if (chunk >= 0) {
path += QLatin1Char('/') + QString::number(chunk);
// We need to do add leading 0 because the server orders the chunk alphabetically
path += QLatin1Char('/') + QString::number(chunk).rightJustified(8, '0');
}
return Utility::concatUrlPath(propagator()->account()->url(), path);
}
@@ -108,9 +109,11 @@ void PropagateUploadFileNG::slotPropfindIterate(const QString &name, const QMap<
return; // skip the info about the path itself
}
bool ok = false;
auto chunkId = name.mid(name.lastIndexOf('/')+1).toUInt(&ok);
QString chunkName = name.mid(name.lastIndexOf('/')+1);
auto chunkId = chunkName.toUInt(&ok);
if (ok) {
_serverChunks[chunkId] = properties["getcontentlength"].toULongLong();
ServerChunkInfo chunkinfo = { properties["getcontentlength"].toULongLong(), chunkName };
_serverChunks[chunkId] = chunkinfo;
}
}
@@ -123,7 +126,7 @@ void PropagateUploadFileNG::slotPropfindFinished()
_currentChunk = 0;
_sent = 0;
while (_serverChunks.contains(_currentChunk)) {
_sent += _serverChunks[_currentChunk];
_sent += _serverChunks[_currentChunk].size;
_serverChunks.remove(_currentChunk);
++_currentChunk;
}
@@ -141,7 +144,7 @@ void PropagateUploadFileNG::slotPropfindFinished()
qDebug() << "Resuming "<< _item->_file << " from chunk " << _currentChunk << "; sent ="<< _sent;
if (!_serverChunks.isEmpty()) {
qDebug() << "To Delete" << _serverChunks;
qDebug() << "To Delete" << _serverChunks.keys();
propagator()->_activeJobList.append(this);
_removeJobError = false;
@@ -149,7 +152,7 @@ void PropagateUploadFileNG::slotPropfindFinished()
// we should remove the later chunks. Otherwise when we do dynamic chunk sizing, we may end up
// with corruptions if there are too many chunks, or if we abort and there are still stale chunks.
for (auto it = _serverChunks.begin(); it != _serverChunks.end(); ++it) {
auto job = new DeleteJob(propagator()->account(), Utility::concatUrlPath(chunkUrl(), QString::number(it.key())), this);
auto job = new DeleteJob(propagator()->account(), Utility::concatUrlPath(chunkUrl(), it->originalName), this);
QObject::connect(job, SIGNAL(finishedSignal()), this, SLOT(slotDeleteJobFinished()));
_jobs.append(job);
job->start();
+3 -2
Ver Arquivo
@@ -589,9 +589,10 @@ public:
char payload = '*';
do {
if (!sourceFolder->children.contains(QString::number(count)))
QString chunkName = QString::number(count).rightJustified(8, '0');
if (!sourceFolder->children.contains(chunkName))
break;
auto &x = sourceFolder->children[QString::number(count)];
auto &x = sourceFolder->children[chunkName];
Q_ASSERT(!x.isDir);
Q_ASSERT(x.size > 0); // There should not be empty chunks
size += x.size;
+3 -3
Ver Arquivo
@@ -3038,17 +3038,17 @@ Es ist nicht ratsam, diese zu benutzen.</translation>
<message>
<location filename="../src/libsync/syncengine.cpp" line="444"/>
<source>File names ending with a period are not supported on this file system.</source>
<translation type="unfinished"/>
<translation>Dateinamen enden mit einem Periode, die in diesem Dateisystem nicht unterstützt wird.</translation>
</message>
<message>
<location filename="../src/libsync/syncengine.cpp" line="454"/>
<source>File names containing the character &apos;%1&apos; are not supported on this file system.</source>
<translation type="unfinished"/>
<translation>Dateinamen beinhalten das Zeichen &apos;%1&apos; und diese werden in diesem Dateisystems nicht unterstützt.</translation>
</message>
<message>
<location filename="../src/libsync/syncengine.cpp" line="457"/>
<source>The file name is a reserved name on this file system.</source>
<translation type="unfinished"/>
<translation>Der Dateiname ist ein reservierter Name in diesem Dateisystem.</translation>
</message>
<message>
<location filename="../src/libsync/syncengine.cpp" line="462"/>
+3 -3
Ver Arquivo
@@ -3045,17 +3045,17 @@ We adviseren deze site niet te gebruiken.</translation>
<message>
<location filename="../src/libsync/syncengine.cpp" line="444"/>
<source>File names ending with a period are not supported on this file system.</source>
<translation type="unfinished"/>
<translation>Bestandsnamen die eindigen met een punt worden niet ondersteund door het bestandssysteem.</translation>
</message>
<message>
<location filename="../src/libsync/syncengine.cpp" line="454"/>
<source>File names containing the character &apos;%1&apos; are not supported on this file system.</source>
<translation type="unfinished"/>
<translation>Bestandsnamen met een &apos;%1&apos; symbool worden niet ondersteund door het bestandssysteem.</translation>
</message>
<message>
<location filename="../src/libsync/syncengine.cpp" line="457"/>
<source>The file name is a reserved name on this file system.</source>
<translation type="unfinished"/>
<translation>De bestandsnaam is een gereserveerde naam op dit bestandssysteem.</translation>
</message>
<message>
<location filename="../src/libsync/syncengine.cpp" line="462"/>
+4 -4
Ver Arquivo
@@ -1234,7 +1234,7 @@ Continuing the sync as normal will cause all your files to be overwritten by an
<message>
<location filename="../src/gui/creds/httpcredentialsgui.cpp" line="35"/>
<source>Please enter %1 password:&lt;br&gt;&lt;br&gt;User: %2&lt;br&gt;Account: %3&lt;br&gt;</source>
<translation type="unfinished"/>
<translation>Por favor, insira a palavra-passe %1:&lt;br&gt;&lt;br&gt;Utilizador: %2&lt;br&gt;Conta: %3&lt;br&gt;</translation>
</message>
<message>
<location filename="../src/gui/creds/httpcredentialsgui.cpp" line="48"/>
@@ -1249,7 +1249,7 @@ Continuing the sync as normal will cause all your files to be overwritten by an
<message>
<location filename="../src/gui/creds/httpcredentialsgui.cpp" line="78"/>
<source>&lt;a href=&quot;%1&quot;&gt;Click here&lt;/a&gt; to request an app password from the web interface.</source>
<translation type="unfinished"/>
<translation>&lt;a href=&quot;%1&quot;&gt;Clique aqui&lt;/a&gt; para solicitar uma palavra-passe da aplicação a partir da interface da Web.</translation>
</message>
</context>
<context>
@@ -1964,7 +1964,7 @@ Não é aconselhada a sua utilização.</translation>
<message>
<location filename="../src/libsync/propagatedownload.cpp" line="379"/>
<source>The download would reduce free disk space below %1</source>
<translation>A transferência poderá reduzir o espaço livre em disco para abaixo de %1</translation>
<translation>A transferência poderá reduzir o espaço livre em disco para menos de %1</translation>
</message>
<message>
<location filename="../src/libsync/propagatedownload.cpp" line="384"/>
@@ -3796,7 +3796,7 @@ Não é aconselhada a sua utilização.</translation>
<message>
<location filename="../src/libsync/progressdispatcher.cpp" line="70"/>
<source>uploading</source>
<translation>A enviar</translation>
<translation>a enviar</translation>
</message>
<message>
<location filename="../src/libsync/progressdispatcher.cpp" line="72"/>
+3 -3
Ver Arquivo
@@ -3039,17 +3039,17 @@ It is not advisable to use it.</source>
<message>
<location filename="../src/libsync/syncengine.cpp" line="444"/>
<source>File names ending with a period are not supported on this file system.</source>
<translation type="unfinished"/>
<translation>Os nomes de arquivos que terminam com um ponto não são suportados neste sistema de arquivos.</translation>
</message>
<message>
<location filename="../src/libsync/syncengine.cpp" line="454"/>
<source>File names containing the character &apos;%1&apos; are not supported on this file system.</source>
<translation type="unfinished"/>
<translation>Os nomes de arquivos que contêm o caractere &apos;%1&apos; não são suportados neste sistema de arquivos.</translation>
</message>
<message>
<location filename="../src/libsync/syncengine.cpp" line="457"/>
<source>The file name is a reserved name on this file system.</source>
<translation type="unfinished"/>
<translation>O nome do arquivo é um nome reservado neste sistema de arquivos.</translation>
</message>
<message>
<location filename="../src/libsync/syncengine.cpp" line="462"/>
+3 -3
Ver Arquivo
@@ -3037,17 +3037,17 @@ It is not advisable to use it.</source>
<message>
<location filename="../src/libsync/syncengine.cpp" line="444"/>
<source>File names ending with a period are not supported on this file system.</source>
<translation type="unfinished"/>
<translation>Эта файловая система не поддерживает имена файлов, оканчивающиеся на точку.</translation>
</message>
<message>
<location filename="../src/libsync/syncengine.cpp" line="454"/>
<source>File names containing the character &apos;%1&apos; are not supported on this file system.</source>
<translation type="unfinished"/>
<translation>Эта файловая система не поддерживает имена файлов, содержащие символ &apos;%1&apos;.</translation>
</message>
<message>
<location filename="../src/libsync/syncengine.cpp" line="457"/>
<source>The file name is a reserved name on this file system.</source>
<translation type="unfinished"/>
<translation>Данное имя файла зарезервировано в данной файловой системе.</translation>
</message>
<message>
<location filename="../src/libsync/syncengine.cpp" line="462"/>