Commit 3629e091 authored by Maurice Kalinowski's avatar Maurice Kalinowski

refactor and add support for pastebin.com

- created protocol class as basis for different paste servers
- removed custom classes and replaced functionality with
  simple QHttp* usage
- removed poster and fetcher classes copied from cpaster
  application. It not getting updated anyways in creator
- Known issue: Listing does not update, when user changes
  protocol
- TODO: add pastebin.ca support. Code is done already, just
  needs to be placed inside plugin.
parent 6e38027b
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://www.qtsoftware.com/contact.
**
**************************************************************************/
#include "codepasterprotocol.h"
#include "codepastersettings.h"
#include "cpasterplugin.h"
#include "cgi.h"
#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>
#include <coreplugin/messagemanager.h>
#include <coreplugin/messageoutputwindow.h>
using namespace CodePaster;
using namespace Core;
CodePasterProtocol::CodePasterProtocol()
{
m_page = new CodePaster::CodePasterSettingsPage();
connect(&http, SIGNAL(responseHeaderReceived(const QHttpResponseHeader &)),
this, SLOT(readPostResponseHeader(const QHttpResponseHeader&)));
}
CodePasterProtocol::~CodePasterProtocol()
{
}
QString CodePasterProtocol::name() const
{
return "CodePaster";
}
bool CodePasterProtocol::canList() const
{
return true;
}
void CodePasterProtocol::fetch(const QString &id)
{
QString link = "http://";
link.append(m_page->hostName());
link.append("/?format=raw&id=");
link.append(id);
QUrl url(link);
QNetworkRequest r(url);
reply = manager.get(r);
connect(reply, SIGNAL(finished()), this, SLOT(fetchFinished()));
fetchId = id;
}
void CodePasterProtocol::list(QListWidget *listWidget)
{
this->listWidget = listWidget;
QString link = QLatin1String("http://");
link += m_page->hostName();
link += QLatin1String("/?command=browse&format=raw");
QUrl url(link);
QNetworkRequest r(url);
listReply = manager.get(r);
connect(listReply, SIGNAL(finished()), this, SLOT(listFinished()));
}
void CodePasterProtocol::paste(const QString &text,
const QString &username,
const QString &comment,
const QString &description)
{
QByteArray data = "command=processcreate&submit=submit&highlight_type=0&description=";
data += CGI::encodeURL(description).toLatin1();
data += "&comment=";
data += CGI::encodeURL(comment).toLatin1();
data += "&code=";
data += CGI::encodeURL(text).toLatin1();
data += "&poster=";
data += CGI::encodeURL(username).toLatin1();
http.setHost(m_page->hostName());
http.post("/", data);
}
bool CodePasterProtocol::hasSettings() const
{
return true;
}
Core::IOptionsPage* CodePasterProtocol::settingsPage()
{
return m_page;
}
void CodePasterProtocol::fetchFinished()
{
if (reply->error()) {
ICore::instance()->messageManager()->printToOutputPane(reply->errorString(), true);
} else {
QString data = reply->readAll();
if (data.contains("<B>No such paste!</B>"))
ICore::instance()->messageManager()->printToOutputPane(tr("No such paste"), true);
QString title = QString::fromLatin1("Codepaster: %1").arg(fetchId);
EditorManager::instance()->newFile(Core::Constants::K_DEFAULT_TEXT_EDITOR, &title, data);
}
reply->deleteLater();
reply = 0;
}
void CodePasterProtocol::listFinished()
{
if (listReply->error()) {
ICore::instance()->messageManager()->printToOutputPane(reply->errorString(), true);
} else {
QByteArray data = listReply->readAll();
listWidget->clear();
QStringList lines = QString(data).split(QLatin1Char('\n'));
listWidget->addItems(lines);
listWidget = 0;
}
listReply->deleteLater();
listReply = 0;
}
void CodePasterProtocol::readPostResponseHeader(const QHttpResponseHeader &header)
{
QString link = header.value("location");
if (!link.isEmpty())
emit pasteDone(link);
}
......@@ -27,49 +27,52 @@
**
**************************************************************************/
#include "fetcher.h"
#include "cgi.h"
#ifndef CODEPASTERPROTOCOL_H
#define CODEPASTERPROTOCOL_H
#include "protocol.h"
#include <QCoreApplication>
#include <QByteArray>
#include <QDebug>
#include <QtGui/QListWidget>
#include <QtNetwork/QHttp>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkReply>
Fetcher::Fetcher(const QString &host)
: QHttp(host)
{
m_host = host;
m_status = 0;
m_hadError = false;
connect(this, SIGNAL(requestFinished(int,bool)), SLOT(gotRequestFinished(int,bool)));
connect(this, SIGNAL(readyRead(QHttpResponseHeader)), SLOT(gotReadyRead(QHttpResponseHeader)));
}
namespace CodePaster {
int Fetcher::fetch(const QString &url)
{
// qDebug("Fetcher::fetch(%s)", qPrintable(url));
return QHttp::get(url);
}
class CodePasterSettingsPage;
int Fetcher::fetch(int pasteID)
class CodePasterProtocol : public Protocol
{
return fetch("http://" + m_host + "/?format=raw&id=" + QString::number(pasteID));
}
Q_OBJECT
public:
CodePasterProtocol();
~CodePasterProtocol();
void Fetcher::gotRequestFinished(int, bool error)
{
m_hadError = error;
QCoreApplication::exit(error ? -1 : 0); // ends event-loop
}
QString name() const;
void Fetcher::gotReadyRead(const QHttpResponseHeader & /* resp */)
{
m_body += QHttp::readAll();
bool canList() const;
bool hasSettings() const;
Core::IOptionsPage* settingsPage();
void fetch(const QString &id);
void list(QListWidget *listWidget);
void paste(const QString &text,
const QString &username = "",
const QString &comment = "",
const QString &description = "");
public slots:
void fetchFinished();
void listFinished();
void readPostResponseHeader(const QHttpResponseHeader&);
private:
CodePasterSettingsPage *m_page;
QHttp http;
QNetworkAccessManager manager;
QNetworkReply* reply;
QNetworkReply* listReply;
QListWidget* listWidget;
QString fetchId;
};
// Hackish check for No Such Paste, as codepaster doesn't send a HTTP code indicating such, or
// sends a redirect to an url indicating failure...
if (m_body.contains("<B>No such paste!</B>")) {
m_body.clear();
m_status = -1;
m_hadError = true;
}
}
#endif // CODEPASTERPROTOCOL_H
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://www.qtsoftware.com/contact.
**
**************************************************************************/
#include "codepastersettings.h"
#include <coreplugin/icore.h>
#include <QtCore/QSettings>
#include <QtGui/QLineEdit>
#include <QtGui/QFileDialog>
#include <QtCore/QDebug>
#include <QtCore/QVariant>
using namespace CodePaster;
CodePasterSettingsPage::CodePasterSettingsPage()
{
m_settings = Core::ICore::instance()->settings();
if (m_settings) {
m_settings->beginGroup("CodePasterSettings");
m_host = m_settings->value("Server", "").toString();
m_settings->endGroup();
}
}
QString CodePasterSettingsPage::id() const
{
return QLatin1String("CodePaster");
}
QString CodePasterSettingsPage::trName() const
{
return tr("CodePaster");
}
QString CodePasterSettingsPage::category() const
{
return QLatin1String("CodePaster");
}
QString CodePasterSettingsPage::trCategory() const
{
return tr("CodePaster");
}
QWidget *CodePasterSettingsPage::createPage(QWidget *parent)
{
QWidget *w = new QWidget(parent);
QLabel *label = new QLabel(tr("Server:"));
QLineEdit *lineedit = new QLineEdit;
lineedit->setText(m_host);
connect(lineedit, SIGNAL(textChanged(QString)), this, SLOT(serverChanged(QString)));
QGridLayout* layout = new QGridLayout();
layout->addWidget(label, 0, 0);
layout->addWidget(lineedit, 0, 1);
w->setLayout(layout);
return w;
}
void CodePasterSettingsPage::apply()
{
if (!m_settings)
return;
m_settings->beginGroup("CodePasterSettings");
m_settings->setValue("Server", m_host);
m_settings->endGroup();
}
void CodePasterSettingsPage::serverChanged(const QString &host)
{
m_host = host;
}
QString CodePasterSettingsPage::hostName() const
{
return m_host;
}
/**************************************************************************
**
** This file is part of Qt Creator
**
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** Commercial Usage
**
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
** GNU Lesser General Public License Usage
**
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at http://www.qtsoftware.com/contact.
**
**************************************************************************/
#ifndef CODEPASTERSETTINGSPAGE_H
#define CODEPASTERSETTINGSPAGE_H
#include "ui_settingspage.h"
#include <coreplugin/dialogs/ioptionspage.h>
#include <QtCore/QStringList>
#include <QtCore/QUrl>
#include <QtGui/QWidget>
QT_BEGIN_NAMESPACE
class QSettings;
QT_END_NAMESPACE
namespace CodePaster {
class CodePasterSettingsPage : public Core::IOptionsPage
{
Q_OBJECT
public:
CodePasterSettingsPage();
QString id() const;
QString trName() const;
QString category() const;
QString trCategory() const;
QWidget *createPage(QWidget *parent);
void apply();
void finish() { }
QString hostName() const;
public slots:
void serverChanged(const QString &host);
private:
QSettings *m_settings;
QString m_host;
};
} // namespace CodePaster
#endif // SETTINGSPAGE_H
QT += network
TEMPLATE = lib
TARGET = CodePaster
include(../../qtcreatorplugin.pri)
include(cpaster_dependencies.pri)
HEADERS += cpasterplugin.h \
settingspage.h
settingspage.h \
protocol.h \
codepasterprotocol.h \
pasteview.h \
codepastersettings.h \
pastebindotcomprotocol.h \
pastebindotcomsettings.h
SOURCES += cpasterplugin.cpp \
settingspage.cpp
settingspage.cpp \
protocol.cpp \
codepasterprotocol.cpp \
pasteview.cpp \
codepastersettings.cpp \
pastebindotcomprotocol.cpp \
pastebindotcomsettings.cpp
FORMS += settingspage.ui \
pasteselect.ui
pasteselect.ui \
pasteview.ui \
pastebindotcomsettings.ui
include(../../shared/cpaster/cpaster.pri)
......@@ -32,7 +32,9 @@
#include "ui_pasteselect.h"
#include "splitter.h"
#include "view.h"
#include "pasteview.h"
#include "codepasterprotocol.h"
#include "pastebindotcomprotocol.h"
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/coreconstants.h>
......@@ -59,7 +61,7 @@ using namespace Core;
using namespace TextEditor;
CodepasterPlugin::CodepasterPlugin()
: m_settingsPage(0), m_fetcher(0), m_poster(0)
: m_settingsPage(0)
{
}
......@@ -70,6 +72,8 @@ CodepasterPlugin::~CodepasterPlugin()
delete m_settingsPage;
m_settingsPage = 0;
}
foreach(Protocol* item, m_protocols)
removeObject(item->settingsPage());
}
bool CodepasterPlugin::initialize(const QStringList &arguments, QString *error_message)
......@@ -85,6 +89,18 @@ bool CodepasterPlugin::initialize(const QStringList &arguments, QString *error_m
m_settingsPage = new SettingsPage();
addObject(m_settingsPage);
// Create the protocols and append them to the Settings
Protocol *protos[] = { new CodePasterProtocol(),
new PasteBinDotComProtocol(),
0};
for(int i=0; protos[i] != 0; ++i) {
connect(protos[i], SIGNAL(pasteDone(QString)), this, SLOT(finishPost(QString)));
m_settingsPage->addProtocol(protos[i]->name());
if (protos[i]->hasSettings())
addObject(protos[i]->settingsPage());
m_protocols.append(protos[i]);
}
//register actions
Core::ActionManager *actionManager = ICore::instance()->actionManager();
......@@ -121,23 +137,8 @@ void CodepasterPlugin::extensionsInitialized()
{
}
QString CodepasterPlugin::serverUrl() const
{
QString url = m_settingsPage->serverUrl().toString();
if (url.startsWith("http://"))
url = url.mid(7);
if (url.endsWith('/'))
url.chop(1);
return url;
}
void CodepasterPlugin::post()
{
// FIXME: The whole m_poster thing is de facto a simple function call.
if (m_poster) {
delete m_poster;
m_poster = 0;
}
IEditor* editor = EditorManager::instance()->currentEditor();
ITextEditor* textEditor = qobject_cast<ITextEditor*>(editor);
if (!textEditor)
......@@ -170,8 +171,13 @@ void CodepasterPlugin::post()
QString username = m_settingsPage->username();
QString description;
QString comment;
QString protocolName;
PasteView view(0);
foreach (Protocol *p, m_protocols) {
view.addProtocol(p->name(), p->name() == m_settingsPage->defaultProtocol());
}
View view(0);
if (!view.show(username, description, comment, lst))
return; // User canceled post
......@@ -179,12 +185,7 @@ void CodepasterPlugin::post()
description = view.getDescription();
comment = view.getComment();
data = view.getContent();
// Submit to codepaster
m_poster = new CustomPoster(serverUrl(),
m_settingsPage->copyToClipBoard(),
m_settingsPage->displayOutput());
protocolName = view.getProtocol();
// Copied from cpaster. Otherwise lineendings will screw up
if (!data.contains("\r\n")) {
......@@ -193,118 +194,61 @@ void CodepasterPlugin::post()
else if (data.contains('\r'))
data.replace('\r', "\r\n");
}
m_poster->post(description, comment, data, username);
foreach(Protocol *protocol, m_protocols) {
if (protocol->name() == protocolName) {
protocol->paste(data, username, comment, description);
break;
}
}
}
void CodepasterPlugin::fetch()
{
if (m_fetcher) {
delete m_fetcher;
m_fetcher = 0;
}
m_fetcher = new CustomFetcher(serverUrl());
QDialog dialog(ICore::instance()->mainWindow());
Ui_PasteSelectDialog ui;
ui.setupUi(&dialog);
foreach(const Protocol *protocol, m_protocols)
ui.protocolBox->addItem(protocol->name());
ui.protocolBox->setCurrentIndex(ui.protocolBox->findText(m_settingsPage->defaultProtocol()));
ui.listWidget->addItems(QStringList() << tr("Waiting for items"));
ui.listWidget->addItems(QStringList() << tr("This protocol supports no listing"));
ui.listWidget->setSelectionMode(QAbstractItemView::ExtendedSelection);
ui.listWidget->setFrameStyle(QFrame::NoFrame);
m_fetcher->list(ui.listWidget);
// ### TODO2: when we change the protocol, we need to relist
foreach(Protocol *protocol, m_protocols) {
if (protocol->name() == ui.protocolBox->currentText() && protocol->canList()) {
ui.listWidget->clear();
ui.listWidget->addItems(QStringList() << tr("Waiting for items"));
protocol->list(ui.listWidget);
break;
}
}
int result = dialog.exec();
if (!result)
return;
bool ok;
QStringList list = ui.pasteEdit->text().split(QLatin1Char(' '));
int pasteID = !list.isEmpty() ? list.first().toInt(&ok) : -1;
if (!ok || pasteID <= 0)
if (list.isEmpty())
return;
QString pasteID = list.first();
delete m_fetcher;
m_fetcher = new CustomFetcher(serverUrl());
m_fetcher->fetch(pasteID);
}
CustomFetcher::CustomFetcher(const QString &host)
: Fetcher(host)
, m_host(host)
, m_listWidget(0)
, m_id(-1)
, m_customError(false)
{
// cpaster calls QCoreApplication::exit which we want to avoid here
disconnect(this, SIGNAL(requestFinished(int,bool))
,this, SLOT(gotRequestFinished(int,bool)));
connect(this, SIGNAL(requestFinished(int,bool))
, SLOT(customRequestFinished(int,bool)));
}
void CustomFetcher::customRequestFinished(int, bool error)
{
m_customError = error;
if (m_customError || hadError()) {
QMessageBox::warning(0, tr("CodePaster Error")
, tr("Could not fetch code")
, QMessageBox::Ok);
return;
}
QByteArray data = body();
if (!m_listWidget) {
QString title = QString::fromLatin1("CodePaster: %1").arg(m_id);
EditorManager::instance()->newFile(Core::Constants::K_DEFAULT_TEXT_EDITOR, &title, data);
} else {
m_listWidget->clear();
QStringList lines = QString(data).split(QLatin1Char('\n'));
m_listWidget->addItems(lines);
m_listWidget = 0;
// Get Protocol
foreach(Protocol *protocol, m_protocols) {
if (protocol->name() == ui.protocolBox->currentText()) {
protocol->fetch(pasteID);
break;
}
}
}
int CustomFetcher::fetch(int pasteID)
void CodepasterPlugin::finishPost(const QString &link)
{
m_id = pasteID;
return Fetcher::fetch(pasteID);
}
void CustomFetcher::list(QListWidget* list)
{
m_listWidget = list;
QString url = QLatin1String("http://");
url += m_host;
url += QLatin1String("/?command=browse&format=raw");
Fetcher::fetch(url);
}
CustomPoster::CustomPoster(const QString &host, bool copyToClipboard, bool displayOutput)
: Poster(host), m_copy(copyToClipboard), m_output(displayOutput)
{
// cpaster calls QCoreApplication::exit which we want to avoid here
disconnect(this, SIGNAL(requestFinished(int,bool)),
this, SLOT(gotRequestFinished(int,bool)));
connect(this, SIGNAL(requestFinished(int,bool)),
SLOT(customRequestFinished(int,bool)));
}
void CustomPoster::customRequestFinished(int, bool error)
{
if (!error) {
if (m_copy)
QApplication::clipboard()->setText(pastedUrl());
ICore::instance()->messageManager()->printToOutputPane(pastedUrl(), m_output);
} else
QMessageBox::warning(0, tr("CodePaster Error"), tr("Some error occured while posting"), QMessageBox::Ok);
#if 0 // Figure out how to access
Core::Internal::MessageOutputWindow* messageWindow =
ExtensionSystem::PluginManager::instance()->getObject<Core::Internal::MessageOutputWindow>();
if (!messageWindow)
qDebug() << "Pasted at:" << pastedUrl();
messageWindow->append(pastedUrl());
messageWindow->setFocus();