Merge pull request #2819 from stojce/19507

Code Review for Job #19507
This commit is contained in:
Clément Brisset 2014-05-12 10:59:58 -07:00
commit f21f5aded9
10 changed files with 650 additions and 6 deletions

View file

@ -3655,7 +3655,17 @@ void Application::takeSnapshot() {
player->setMedia(QUrl::fromLocalFile(inf.absoluteFilePath()));
player->play();
Snapshot::saveSnapshot(_glWidget, _myAvatar);
QString fileName = Snapshot::saveSnapshot(_glWidget, _myAvatar);
AccountManager& accountManager = AccountManager::getInstance();
if (!accountManager.isLoggedIn()) {
return;
}
if (!_snapshotShareDialog) {
_snapshotShareDialog = new SnapshotShareDialog(fileName, _glWidget);
}
_snapshotShareDialog->show();
}
void Application::urlGoTo(int argc, const char * constArgv[]) {

View file

@ -74,6 +74,7 @@
#include "ui/ModelsBrowser.h"
#include "ui/OctreeStatsDialog.h"
#include "ui/RearMirrorTools.h"
#include "ui/SnapshotShareDialog.h"
#include "ui/LodToolsDialog.h"
#include "ui/LogDialog.h"
#include "ui/UpdateDialog.h"
@ -517,6 +518,7 @@ private:
std::vector<VoxelFade> _voxelFades;
ControllerScriptingInterface _controllerScriptingInterface;
QPointer<LogDialog> _logDialog;
QPointer<SnapshotShareDialog> _snapshotShareDialog;
QString _previousScriptLocation;

View file

@ -16,7 +16,7 @@
#include "XmppClient.h"
const QString DEFAULT_XMPP_SERVER = "chat.highfidelity.io";
const QString DEFAULT_CHAT_ROOM = "public@public-chat.highfidelity.io";
const QString DEFAULT_CHAT_ROOM = "test@public-chat.highfidelity.io";
XmppClient::XmppClient() :
_xmppClient(),

View file

@ -64,7 +64,7 @@ SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) {
return data;
}
void Snapshot::saveSnapshot(QGLWidget* widget, Avatar* avatar) {
QString Snapshot::saveSnapshot(QGLWidget* widget, Avatar* avatar) {
QImage shot = widget->grabFrameBuffer();
glm::vec3 location = avatar->getPosition();
@ -99,6 +99,8 @@ void Snapshot::saveSnapshot(QGLWidget* widget, Avatar* avatar) {
fileName.append(QString(FILENAME_PATH_FORMAT.arg(username, now.toString(DATETIME_FORMAT), formattedLocation)));
shot.save(fileName, 0, 100);
return fileName;
}

View file

@ -41,7 +41,7 @@ private:
class Snapshot {
public:
static void saveSnapshot(QGLWidget* widget, Avatar* avatar);
static QString saveSnapshot(QGLWidget* widget, Avatar* avatar);
static SnapshotMetaData* parseSnapshotData(QString snapshotPath);
};

View file

@ -0,0 +1,198 @@
//
// SnapshotShareDialog.cpp
// interface/src/ui
//
// Created by Stojce Slavkovski on 2/16/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "SnapshotShareDialog.h"
#include "AccountManager.h"
#include <QFile>
#include <QHttpMultiPart>
#include <QJsonArray>
#include <QJsonDocument>
#include <QMessageBox>
#include <QUrlQuery>
const int NARROW_SNAPSHOT_DIALOG_SIZE = 500;
const int WIDE_SNAPSHOT_DIALOG_WIDTH = 650;
const int SUCCESS_LABEL_HEIGHT = 140;
const QString FORUM_URL = "https://alphas.highfidelity.io";
const QString FORUM_UPLOADS_URL = FORUM_URL + "/uploads";
const QString FORUM_POST_URL = FORUM_URL + "/posts";
const QString FORUM_REPLY_TO_TOPIC = "244";
const QString FORUM_POST_TEMPLATE = "<img src='%1'/><p>%2</p>";
const QString SHARE_DEFAULT_ERROR = "The server isn't responding. Please try again in a few minutes.";
const QString SUCCESS_LABEL_TEMPLATE = "Success!!! Go check out your image ...<br/><a style='color:#333;text-decoration:none' href='%1'>%1</a>";
Q_DECLARE_METATYPE(QNetworkAccessManager::Operation)
SnapshotShareDialog::SnapshotShareDialog(QString fileName, QWidget* parent) :
QDialog(parent),
_fileName(fileName),
_networkAccessManager(NULL)
{
setAttribute(Qt::WA_DeleteOnClose);
_ui.setupUi(this);
QPixmap snaphsotPixmap(fileName);
float snapshotRatio = static_cast<float>(snaphsotPixmap.size().width()) / snaphsotPixmap.size().height();
// narrow snapshot
if (snapshotRatio > 1) {
setFixedWidth(WIDE_SNAPSHOT_DIALOG_WIDTH);
_ui.snapshotWidget->setFixedWidth(WIDE_SNAPSHOT_DIALOG_WIDTH);
}
float labelRatio = static_cast<float>(_ui.snapshotWidget->size().width()) / _ui.snapshotWidget->size().height();
// set the same aspect ratio of label as of snapshot
if (snapshotRatio > labelRatio) {
int oldHeight = _ui.snapshotWidget->size().height();
_ui.snapshotWidget->setFixedHeight((int) (_ui.snapshotWidget->size().width() / snapshotRatio));
// if height is less then original, resize the window as well
if (_ui.snapshotWidget->size().height() < NARROW_SNAPSHOT_DIALOG_SIZE) {
setFixedHeight(size().height() - (oldHeight - _ui.snapshotWidget->size().height()));
}
} else {
_ui.snapshotWidget->setFixedWidth((int) (_ui.snapshotWidget->size().height() * snapshotRatio));
}
_ui.snapshotWidget->setPixmap(snaphsotPixmap);
_ui.snapshotWidget->adjustSize();
}
void SnapshotShareDialog::accept() {
uploadSnapshot();
}
void SnapshotShareDialog::uploadSnapshot() {
if (AccountManager::getInstance().getAccountInfo().getDiscourseApiKey().isEmpty()) {
QMessageBox::warning(this, "",
"Your Discourse API key is missing, you cannot share snapshots. Please try to relog.");
return;
}
if (!_networkAccessManager) {
_networkAccessManager = new QNetworkAccessManager(this);
}
QHttpMultiPart* multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart apiKeyPart;
apiKeyPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"api_key\""));
apiKeyPart.setBody(AccountManager::getInstance().getAccountInfo().getDiscourseApiKey().toLatin1());
QFile* file = new QFile(_fileName);
file->open(QIODevice::ReadOnly);
QHttpPart imagePart;
imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/jpeg"));
imagePart.setHeader(QNetworkRequest::ContentDispositionHeader,
QVariant("form-data; name=\"file\"; filename=\"" + file->fileName() +"\""));
imagePart.setBodyDevice(file);
file->setParent(multiPart); // we cannot delete the file now, so delete it with the multiPart
multiPart->append(apiKeyPart);
multiPart->append(imagePart);
QUrl url(FORUM_UPLOADS_URL);
QNetworkRequest request(url);
QNetworkReply* reply = _networkAccessManager->post(request, multiPart);
connect(reply, &QNetworkReply::finished, this, &SnapshotShareDialog::uploadRequestFinished);
QEventLoop loop;
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();
}
void SnapshotShareDialog::sendForumPost(QString snapshotPath) {
if (!_networkAccessManager) {
_networkAccessManager = new QNetworkAccessManager(this);
}
// post to Discourse forum
QNetworkRequest request;
QUrl forumUrl(FORUM_POST_URL);
QUrlQuery query;
query.addQueryItem("api_key", AccountManager::getInstance().getAccountInfo().getDiscourseApiKey());
query.addQueryItem("topic_id", FORUM_REPLY_TO_TOPIC);
query.addQueryItem("raw", FORUM_POST_TEMPLATE.arg(snapshotPath, _ui.textEdit->toPlainText()));
forumUrl.setQuery(query);
QByteArray postData = forumUrl.toEncoded(QUrl::RemoveFragment);
request.setUrl(forumUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
QNetworkReply* requestReply = _networkAccessManager->post(request, postData);
connect(requestReply, &QNetworkReply::finished, this, &SnapshotShareDialog::postRequestFinished);
QEventLoop loop;
connect(requestReply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();
}
void SnapshotShareDialog::postRequestFinished() {
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll());
const QJsonObject& responseObject = jsonResponse.object();
if (responseObject.contains("id")) {
_ui.textEdit->setHtml("");
const QString urlTemplate = "%1/t/%2/%3/%4";
QString link = urlTemplate.arg(FORUM_URL,
responseObject["topic_slug"].toString(),
QString::number(responseObject["topic_id"].toDouble()),
QString::number(responseObject["post_number"].toDouble()));
_ui.successLabel->setText(SUCCESS_LABEL_TEMPLATE.arg(link));
_ui.successLabel->setFixedHeight(SUCCESS_LABEL_HEIGHT);
// hide input widgets
_ui.shareButton->hide();
_ui.textEdit->hide();
_ui.labelNotes->hide();
} else {
QString errorMessage(SHARE_DEFAULT_ERROR);
if (responseObject.contains("errors")) {
QJsonArray errorArray = responseObject["errors"].toArray();
if (!errorArray.first().toString().isEmpty()) {
errorMessage = errorArray.first().toString();
}
}
QMessageBox::warning(this, "", errorMessage);
}
}
void SnapshotShareDialog::uploadRequestFinished() {
QNetworkReply* requestReply = reinterpret_cast<QNetworkReply*>(sender());
QJsonDocument jsonResponse = QJsonDocument::fromJson(requestReply->readAll());
const QJsonObject& responseObject = jsonResponse.object();
if (responseObject.contains("url")) {
sendForumPost(responseObject["url"].toString());
} else {
QMessageBox::warning(this, "", SHARE_DEFAULT_ERROR);
}
delete requestReply;
}

View file

@ -0,0 +1,41 @@
//
// SnapshotShareDialog.h
// interface/src/ui
//
// Created by Stojce Slavkovski on 2/16/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_snapshotShareDialog
#define hifi_snapshotShareDialog
#include "ui_shareSnapshot.h"
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QUrl>
class SnapshotShareDialog : public QDialog {
Q_OBJECT
public:
SnapshotShareDialog(QString fileName, QWidget* parent = 0);
private:
QString _fileName;
QNetworkAccessManager* _networkAccessManager;
Ui_SnapshotShareDialog _ui;
void uploadSnapshot();
void sendForumPost(QString snapshotPath);
private slots:
void uploadRequestFinished();
void postRequestFinished();
void accept();
};
#endif // hifi_snapshotShareDialog

View file

@ -0,0 +1,377 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>SnapshotShareDialog</class>
<widget class="QDialog" name="SnapshotShareDialog">
<property name="windowModality">
<enum>Qt::NonModal</enum>
</property>
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>502</width>
<height>616</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>500</width>
<height>616</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>502</width>
<height>616</height>
</size>
</property>
<property name="windowTitle">
<string>Share with Alphas</string>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(255, 255, 255);</string>
</property>
<property name="modal">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>0</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetMinAndMaxSize</enum>
</property>
<item alignment="Qt::AlignHCenter|Qt::AlignTop">
<widget class="QLabel" name="snapshotWidget">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>500</width>
<height>500</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>30</width>
<height>2000</height>
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color: #ccc;</string>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="indent">
<number>0</number>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>9</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="successLabel">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>0</height>
</size>
</property>
<property name="font">
<font>
<family>Arial</family>
</font>
</property>
<property name="styleSheet">
<string notr="true">color: #666</string>
</property>
<property name="text">
<string/>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
<property name="textInteractionFlags">
<set>Qt::TextBrowserInteraction</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="labelNotes">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>19</height>
</size>
</property>
<property name="font">
<font>
<family>Helvetica</family>
<pointsize>14</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="styleSheet">
<string notr="true">color: #666;
padding-left:20px;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="text">
<string>Notes about this image</string>
</property>
<property name="alignment">
<set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
</property>
<property name="margin">
<number>0</number>
</property>
<property name="indent">
<number>0</number>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>0</number>
</property>
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>19</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QTextEdit" name="textEdit">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>60</height>
</size>
</property>
<property name="font">
<font>
<family>Helvetica</family>
<pointsize>14</pointsize>
</font>
</property>
<property name="styleSheet">
<string notr="true">border: 1px solid #c5c5c5;</string>
</property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="html">
<string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
p, li { white-space: pre-wrap; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Helvetica'; font-size:14pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>25</width>
<height>19</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="shareButton">
<property name="styleSheet">
<string notr="true"> background-color: #333333;
border-width: 0;
border-radius: 9px;
border-radius: 9px;
font-family: Arial;
font-size: 18px;
font-weight: 100;
color: #FFFFFF;
width: 120px;
height: 50px;</string>
</property>
<property name="text">
<string>Share</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>25</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>11</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>shareButton</sender>
<signal>clicked()</signal>
<receiver>SnapshotShareDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>420</x>
<y>565</y>
</hint>
<hint type="destinationlabel">
<x>250</x>
<y>307</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -16,7 +16,8 @@
DataServerAccountInfo::DataServerAccountInfo() :
_accessToken(),
_username(),
_xmppPassword()
_xmppPassword(),
_discourseApiKey()
{
}
@ -29,12 +30,14 @@ DataServerAccountInfo::DataServerAccountInfo(const QJsonObject& jsonObject) :
QJsonObject userJSONObject = jsonObject["user"].toObject();
setUsername(userJSONObject["username"].toString());
setXMPPPassword(userJSONObject["xmpp_password"].toString());
setDiscourseApiKey(userJSONObject["discourse_api_key"].toString());
}
DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherInfo) {
_accessToken = otherInfo._accessToken;
_username = otherInfo._username;
_xmppPassword = otherInfo._xmppPassword;
_discourseApiKey = otherInfo._discourseApiKey;
}
DataServerAccountInfo& DataServerAccountInfo::operator=(const DataServerAccountInfo& otherInfo) {
@ -49,6 +52,7 @@ void DataServerAccountInfo::swap(DataServerAccountInfo& otherInfo) {
swap(_accessToken, otherInfo._accessToken);
swap(_username, otherInfo._username);
swap(_xmppPassword, otherInfo._xmppPassword);
swap(_discourseApiKey, otherInfo._discourseApiKey);
}
void DataServerAccountInfo::setUsername(const QString& username) {
@ -65,6 +69,12 @@ void DataServerAccountInfo::setXMPPPassword(const QString& xmppPassword) {
}
}
void DataServerAccountInfo::setDiscourseApiKey(const QString& discourseApiKey) {
if (_discourseApiKey != discourseApiKey) {
_discourseApiKey = discourseApiKey;
}
}
QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) {
out << info._accessToken << info._username << info._xmppPassword;
return out;

View file

@ -31,7 +31,10 @@ public:
const QString& getXMPPPassword() const { return _xmppPassword; }
void setXMPPPassword(const QString& xmppPassword);
const QString& getDiscourseApiKey() const { return _discourseApiKey; }
void setDiscourseApiKey(const QString& discourseApiKey);
friend QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info);
friend QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info);
private:
@ -40,6 +43,7 @@ private:
OAuthAccessToken _accessToken;
QString _username;
QString _xmppPassword;
QString _discourseApiKey;
};
#endif // hifi_DataServerAccountInfo_h