Fix i18n => IMAP Server:
[kdepim.git] / blogilo / src / postentry.cpp
blob35f587ebfcab9619feae3545db9e87210f2b1e87
1 /*
2 This file is part of Blogilo, A KDE Blogging Client
4 Copyright (C) 2008-2010 Mehrdad Momeny <mehrdad.momeny@gmail.com>
5 Copyright (C) 2008-2010 Golnaz Nilieh <g382nilieh@gmail.com>
6 Copyright (C) 2013-2016 Laurent Montel <montel@kde.org>
8 This program is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License as
10 published by the Free Software Foundation; either version 2 of
11 the License or (at your option) version 3 or any later version
12 accepted by the membership of KDE e.V. (or its successor approved
13 by the membership of KDE e.V.), which shall act as a proxy
14 defined in Section 14 of version 3 of the license.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, see http://www.gnu.org/licenses/
25 #include "postentry.h"
26 #include "bilbomedia.h"
27 #include "backend.h"
28 #include "dbman.h"
29 #include "global.h"
30 #include "sendtoblogdialog.h"
31 #include "settings.h"
32 #include "bilboblog.h"
33 #include "syncuploader.h"
35 #include "composer/webengine/blogilocomposerwebengineeditor.h"
36 #include "composer/webengine/blogilocomposerwebengineview.h"
37 #include "composer/webengine/blogilocomposerwebenginewidget.h"
38 #include "composer/bilbobrowser.h"
39 #include "composer/htmleditor.h"
41 #include <PimCommon/SpellCheckLineEdit>
43 #include "blogilo_debug.h"
44 #include <klocalizedstring.h>
45 #include <klineedit.h>
46 #include <KMessageBox>
47 #include <ktexteditor/view.h>
48 #include <ktexteditor/document.h>
49 #include <kio/job.h>
50 #include <QTabWidget>
52 #include <QProgressBar>
53 #include <QLabel>
54 #include <QTimer>
55 #include <QLayout>
56 #include <QHBoxLayout>
58 #define MINUTE 60000
60 class PostEntry::Private
62 public:
63 QPointer<QProgressBar> progress;
64 QGridLayout *gridLayout;
65 QHBoxLayout *horizontalLayout;
66 QLabel *labelTitle;
67 PimCommon::SpellCheckLineEdit *txtTitle;
68 QTimer *mTimer;
69 BilboPost mCurrentPost;
70 int mCurrentPostBlogId;
71 QMap <QString, BilboMedia *> mMediaList;
73 int mNumOfFilesToBeUploaded;
74 bool isUploadingMediaFilesFailed;
75 bool isNewPost;
76 bool isPostContentModified;
78 QTabWidget *tabWidget;
79 QWidget *tabVisual;
80 QWidget *tabHtml;
81 QWidget *tabPreview;
82 BlogiloComposerWebEngineWidget *wysiwygEditor;
83 KTextEditor::View *htmlEditor;
84 BilboBrowser *previewer;
86 int prev_index;
89 PostEntry::PostEntry(QWidget *parent)
90 : QFrame(parent), d(new Private)
92 createUi();
93 //FIXME connect(d->wysiwygEditor->editor(), &ComposerEditorNG::ComposerEditor::textChanged, this, &PostEntry::textChanged);
94 connect(d->htmlEditor->document(), &KTextEditor::Document::textChanged,
95 this, &PostEntry::textChanged);
96 layout()->addWidget(d->tabWidget);
97 d->mTimer = new QTimer(this);
98 d->mTimer->start(Settings::autosaveInterval() * MINUTE);
99 connect(d->mTimer, &QTimer::timeout, this, &PostEntry::saveTemporary);
100 d->progress = Q_NULLPTR;
101 d->mCurrentPostBlogId = -1;
102 d->mNumOfFilesToBeUploaded = 0;
103 d->isPostContentModified = false;
104 connect(this, &PostEntry::textChanged, this, &PostEntry::slotPostModified);
107 PostEntry::~PostEntry()
109 disconnect(d->txtTitle, &PimCommon::SpellCheckLineEdit::textChanged, this, &PostEntry::slotTitleChanged);
110 disconnect(d->txtTitle, &PimCommon::SpellCheckLineEdit::focusDown, this, &PostEntry::slotFocusEditor);
112 delete d;
115 void PostEntry::settingsChanged()
118 d->mTimer->setInterval(Settings::autosaveInterval() * MINUTE);
119 if (Settings::autosaveInterval()) {
120 d->mTimer->start();
121 } else {
122 d->mTimer->stop();
126 void PostEntry::createUi()
128 d->tabWidget = new QTabWidget(this);
129 d->tabVisual = new QWidget(d->tabWidget);
130 d->tabHtml = new QWidget(d->tabWidget);
131 d->tabPreview = new QWidget(d->tabWidget);
132 d->tabWidget->addTab(d->tabVisual, i18nc("Software", "Visual Editor"));
133 d->tabWidget->addTab(d->tabHtml, i18nc("Software", "HTML Editor"));
134 d->tabWidget->addTab(d->tabPreview, i18nc("preview of the edited post", "Post Preview"));
135 connect(d->tabWidget, &QTabWidget::currentChanged, this, &PostEntry::slotSyncEditors);
136 d->prev_index = 0;
138 /// WYSIWYG Editor:
140 BlogiloComposerWebEngineView *view = new BlogiloComposerWebEngineView(this);
141 d->wysiwygEditor = new BlogiloComposerWebEngineWidget(view/*,d->tabVisual*/);
142 QVBoxLayout *vLayout = new QVBoxLayout(d->tabVisual);
143 vLayout->addWidget(d->wysiwygEditor);
145 ///htmlEditor:
146 d->htmlEditor = HtmlEditor::self()->createView(d->tabHtml);
147 QGridLayout *hLayout = new QGridLayout(d->tabHtml);
148 hLayout->addWidget(d->htmlEditor);
150 ///previewer:
151 d->previewer = new BilboBrowser(d->tabPreview);
152 QGridLayout *gLayout = new QGridLayout(d->tabPreview);
153 gLayout->addWidget(d->previewer);
155 connect(d->previewer, &BilboBrowser::sigSetBlogStyle, this, &PostEntry::slotSetPostPreview);
157 d->tabWidget->setCurrentIndex(0);
159 d->gridLayout = new QGridLayout(this);
161 d->horizontalLayout = new QHBoxLayout();
162 d->horizontalLayout->setSizeConstraint(QLayout::SetDefaultConstraint);
164 d->labelTitle = new QLabel(this);
165 d->labelTitle->setText(i18nc("noun, the post title", "Title:"));
166 d->horizontalLayout->addWidget(d->labelTitle);
168 d->txtTitle = new PimCommon::SpellCheckLineEdit(this, QStringLiteral("blogilorc"));
169 d->horizontalLayout->addWidget(d->txtTitle);
170 d->labelTitle->setBuddy(d->txtTitle);
171 connect(d->txtTitle, &PimCommon::SpellCheckLineEdit::textChanged, this, &PostEntry::slotTitleChanged);
172 connect(d->txtTitle, &PimCommon::SpellCheckLineEdit::focusDown, this, &PostEntry::slotFocusEditor);
174 d->gridLayout->addLayout(d->horizontalLayout, 0, 0, 1, 1);
177 void PostEntry::slotFocusEditor()
179 switch (d->tabWidget->currentIndex()) {
180 case 0:
181 d->wysiwygEditor->editor()->startEditing();
182 break;
183 case 1:
184 d->htmlEditor->setFocus();
185 break;
189 void PostEntry::slotSyncEditors(int index)
191 if (index == 0) {
192 if (d->prev_index == 2) {
193 d->previewer->stop();
194 d->prev_index = index;
195 return;
196 }//An else clause can do the job of goto, No? -Mehrdad :D
197 d->wysiwygEditor->editor()->setHtmlContent(d->htmlEditor->document()->text());
198 d->wysiwygEditor->editor()->setFocus();
199 d->wysiwygEditor->editor()->startEditing();
200 } else if (index == 1) {
201 if (d->prev_index == 2) {
202 d->previewer->stop();
203 d->prev_index = index;
204 return;
206 d->htmlEditor->document()->setText(d->wysiwygEditor->editor()->htmlContent());
207 d->htmlEditor->setFocus();
208 } else {
209 if (d->prev_index == 1) {
210 d->wysiwygEditor->editor()->setHtmlContent(d->htmlEditor->document()->text());
211 } else {
212 d->htmlEditor->document()->setText(d->wysiwygEditor->editor()->htmlContent());
214 d->previewer->setHtml(d->txtTitle->toPlainText(), d->htmlEditor->document()->text());
216 d->prev_index = index;
219 void PostEntry::slotSetPostPreview()
221 if (d->tabWidget->currentIndex() == 2) {
222 d->previewer->setHtml(d->txtTitle->toPlainText(), d->htmlEditor->document()->text());
226 QString PostEntry::htmlContent() const
228 if (d->tabWidget->currentIndex() == 1) {
229 d->wysiwygEditor->editor()->setHtmlContent(d->htmlEditor->document()->text());
230 } else {
231 #if 0
232 d->htmlEditor->document()->setText(d->wysiwygEditor->editor()->htmlContent());
233 #endif
235 return d->htmlEditor->document()->text();
238 void PostEntry::setHtmlContent(const QString &content)
240 d->wysiwygEditor->editor()->setHtmlContent(content);
241 d->htmlEditor->document()->setText(content);
244 void PostEntry::slotTitleChanged()
246 const QString titleText(d->txtTitle->toPlainText());
247 d->mCurrentPost.setTitle(titleText);
248 Q_EMIT postTitleChanged(titleText);
251 QString PostEntry::postTitle() const
253 return d->mCurrentPost.title();
256 void PostEntry::setPostTitle(const QString &title)
258 d->txtTitle->setPlainText(title);
259 d->mCurrentPost.setTitle(title);
262 void PostEntry::setPostBody(const QString &content, const QString &additionalContent)
264 QString body;
265 if (additionalContent.isEmpty()) {
266 body = content;
267 } else {
268 body = content + QLatin1String("<hr/><!--split-->") + additionalContent;
269 d->mCurrentPost.setAdditionalContent(QString());
271 // if (body.isEmpty()){
272 // body = "<p></p>";//This is because of Bug #387578
273 // }
274 d->mCurrentPost.setContent(body);
275 setHtmlContent(body);
276 d->isPostContentModified = false;
277 connect(this, &PostEntry::textChanged, this, &PostEntry::slotPostModified);
278 // connect( txtTitle, SIGNAL(textChanged(QString)), this, SLOT(slotPostModified()) );
281 int PostEntry::currentPostBlogId() const
283 return d->mCurrentPostBlogId;
286 void PostEntry::setCurrentPostBlogId(int blog_id)
288 d->mCurrentPostBlogId = blog_id;
289 if (blog_id != -1 && DBMan::self()->blogList().contains(blog_id)) {
290 setDefaultLayoutDirection(DBMan::self()->blogList().value(blog_id)->direction());
294 void PostEntry::setCurrentPostFromEditor()
296 if (d->isPostContentModified) {
298 const QString &str = htmlContent();
299 d->mCurrentPost.setContent(str);
300 d->isPostContentModified = false;
301 connect(this, &PostEntry::textChanged, this, &PostEntry::slotPostModified);
305 BilboPost *PostEntry::currentPost()
307 setCurrentPostFromEditor();
308 return &d->mCurrentPost;
311 void PostEntry::setCurrentPost(const BilboPost &post)
313 d->mCurrentPost = post;
314 qCDebug(BLOGILO_LOG) << "local_id: " << d->mCurrentPost.localId();
315 this->setPostBody(d->mCurrentPost.content(), d->mCurrentPost.additionalContent());
316 this->setPostTitle(d->mCurrentPost.title());
319 Qt::LayoutDirection PostEntry::defaultLayoutDirection() const
321 return d->txtTitle->layoutDirection();
324 void PostEntry::setDefaultLayoutDirection(Qt::LayoutDirection direction)
327 d->tabWidget->setLayoutDirection(direction);
328 d->txtTitle->setLayoutDirection(direction);
331 QList< BilboMedia * > PostEntry::localImages() const
333 return d->wysiwygEditor->editor()->getLocalImages();
336 void PostEntry::replaceImageSrc(const QString &src, const QString &dest)
338 d->wysiwygEditor->editor()->replaceImageSrc(src, dest);
341 bool PostEntry::uploadMediaFiles(Backend *backend)
343 bool localBackend = false;
344 bool result = true;
345 if (!backend) {
346 localBackend = true;
347 backend = new Backend(d->mCurrentPostBlogId, this);
349 QList<BilboMedia *> lImages = localImages();
350 if (!lImages.isEmpty()) {
351 showProgressBar();
352 QList<BilboMedia *>::iterator it = lImages.begin();
353 QList<BilboMedia *>::iterator endIt = lImages.end();
354 for (; it != endIt; ++it) {
355 BilboMedia *media = (*it);
356 SyncUploader *uploader = new SyncUploader(this);
357 if (uploader->uploadMedia(backend, media)) {
358 replaceImageSrc(media->localUrl().url(),
359 media->remoteUrl().url());
360 } else {
361 const QString err = i18n("Uploading the media file %1 failed.\n%2",
362 media->name(), uploader->errorMessage());
363 Q_EMIT postPublishingDone(true, err);
364 result = false;
365 break;
367 uploader->deleteLater();
369 d->mCurrentPost.setContent(htmlContent());
371 if (localBackend) {
372 backend->deleteLater();
374 return result;
377 void PostEntry::slotError(const QString &errMsg)
379 const QString err = i18n("An error occurred in the last transaction.\n%1", errMsg);
380 Q_EMIT postPublishingDone(true, err);
381 deleteProgressBar();
382 sender()->deleteLater();
385 void PostEntry::submitPost(int blogId, const BilboPost &postData)
387 setCurrentPostFromEditor();
388 if (d->mCurrentPost.content().isEmpty() || d->mCurrentPost.title().isEmpty()) {
389 if (KMessageBox::warningContinueCancel(this,
390 i18n("Your post title or body is empty.\nAre you sure you want to submit this post?")
391 ) == KMessageBox::Cancel) {
392 return;
395 bool isNew = false;
396 if (d->mCurrentPost.status() == BilboPost::New) {
397 isNew = true;
399 QPointer<SendToBlogDialog> dia = new SendToBlogDialog(isNew, d->mCurrentPost.isPrivate(), this);
400 dia->setAttribute(Qt::WA_DeleteOnClose, false);
401 if (dia->exec() == QDialog::Accepted) {
402 this->setCursor(Qt::BusyCursor);
403 d->mCurrentPost.setProperties(postData);
404 d->mCurrentPostBlogId = blogId;
406 QString msgType;
407 if (dia->isPrivate()) {
408 msgType = i18nc("Post status, e.g Draft or Published Post", "draft");
409 d->mCurrentPost.setPrivate(true);
410 } else {
411 msgType = i18nc("Post status, e.g Draft or Published Post", "post");
412 d->mCurrentPost.setPrivate(false);
415 QString statusMsg;
416 if (dia->isNew()) {
417 statusMsg = i18n("Submitting new %1...", msgType);
418 d->isNewPost = true;
419 } else {
420 statusMsg = i18n("Modifying %1...", msgType);
421 d->isNewPost = false;
424 Q_EMIT showStatusMessage(statusMsg, true);
425 Backend *b = new Backend(d->mCurrentPostBlogId, this);
426 connect(b, &Backend::sigError, this, &PostEntry::slotError);
427 if (uploadMediaFiles(b)) {
428 qCDebug(BLOGILO_LOG) << "Uploading";
429 showProgressBar();
430 connect(b, &Backend::sigPostPublished, this, &PostEntry::slotPostPublished);
431 if (d->isNewPost) {
432 b->publishPost(&d->mCurrentPost);
433 } else {
434 b->modifyPost(&d->mCurrentPost);
436 } else {
437 deleteProgressBar();
440 delete dia;
443 void PostEntry::slotPostPublished(int blog_id, BilboPost *post)
445 qCDebug(BLOGILO_LOG) << "BlogId: " << blog_id << "Post Id on server: " << post->postId();
446 DBMan::self()->removeTempEntry(d->mCurrentPost);
447 QString msg;
448 setCurrentPost(*post);
449 if (d->mCurrentPost.isPrivate()) {
450 msg = i18n("Draft with title \"%1\" saved successfully.", post->title());
451 } else if (d->mCurrentPost.status() == BilboPost::Modified) {
452 msg = i18n("Post with title \"%1\" modified successfully.", post->title());
453 } else {
454 msg = i18n("Post with title \"%1\" published successfully.", post->title());
456 // KMessageBox::information( this, msg, "Successful" );
457 deleteProgressBar();
458 this->unsetCursor();
459 Q_EMIT postPublishingDone(false, msg);
460 sender()->deleteLater(); //FIXME Check if this command needed or NOT -Mehrdad
463 void PostEntry::showProgressBar()
465 if (!d->progress) {
466 d->progress = new QProgressBar(this);
467 layout()->addWidget(d->progress);
468 d->progress->setRange(0, 0);
472 void PostEntry::deleteProgressBar()
474 if (d->progress) {
475 this->layout()->removeWidget(d->progress);
476 d->progress->deleteLater();
477 d->progress = Q_NULLPTR;
481 void PostEntry::saveLocally()
483 if (currentPost()->content().isEmpty()) {
484 if (KMessageBox::warningYesNo(this, i18n("The current post content is empty, are you sure you want to save an empty post?")) == KMessageBox::No) {
485 return;
488 const int resId = DBMan::self()->saveLocalEntry(*currentPost(), d->mCurrentPostBlogId);
489 if (resId == -1) {
490 KMessageBox::detailedSorry(this, i18n("Saving post locally failed."), DBMan::self()->lastErrorText());
491 return;
493 d->mCurrentPost.setLocalId(resId);
494 Q_EMIT postSavedLocally();
495 Q_EMIT showStatusMessage(i18n("Post saved locally."), false);
496 qCDebug(BLOGILO_LOG) << "Locally saved";
499 void PostEntry::saveTemporary()
501 if (d->isPostContentModified) {
502 const int res = DBMan::self()->saveTempEntry(*currentPost(), d->mCurrentPostBlogId);
503 if (res != -1) {
504 d->mCurrentPost.setLocalId(res);
505 Q_EMIT postSavedTemporary();
506 qCDebug(BLOGILO_LOG) << "Temporary saved";
507 } else {
508 qCDebug(BLOGILO_LOG) << "Saving temporary failed: " << DBMan::self()->lastErrorText();
513 void PostEntry::slotPostModified()
516 disconnect(this, &PostEntry::textChanged, this, &PostEntry::slotPostModified);
517 // disconnect( txtTitle, SIGNAL(textChanged(QString)), this, SLOT(slotPostModified()) );
518 // Q_EMIT postModified();
519 d->isPostContentModified = true;