1 /***************************************************************************
2  * KoScriptManager.cpp
3  * This file is part of the KDE project
4  * copyright (c) 2005-2006 Cyrille Berger <cberger@cberger.net>
5  * copyright (C) 2006-2007 Sebastian Sauer <mail@dipe.org>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public License
18  * along with this program; see the file COPYING.  If not, write to
19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  ***************************************************************************/
22 
23 #include "KoScriptManager.h"
24 
25 #include "KoScriptManagerAdd.h"
26 #include "KoKrossDebug.h"
27 
28 #include <kross/core/manager.h>
29 #include <kross/core/actioncollection.h>
30 #include <kross/ui/model.h>
31 #include <kross/ui/view.h>
32 
33 #include <KoIcon.h>
34 
35 #include <klocalizedstring.h>
36 
37 #include <QStandardPaths>
38 #include <QBoxLayout>
39 
40 /******************************************************************************
41  * KoScriptManagerCollection
42  */
43 
44 /*
45 /// \internal class that inherits \a KNewStuffSecure to implement the GHNS-functionality.
46 class KoScriptManagerNewStuff : public KNewStuffSecure
47 {
48     public:
49         KoScriptManagerNewStuff(KoScriptManagerCollection *collection, const QString& type, QWidget *parentWidget = 0)
50             : KNewStuffSecure(type, parentWidget)
51             , m_collection(collection) {}
52         virtual ~KoScriptManagerNewStuff() {}
53     private:
54         KoScriptManagerCollection *m_collection;
55         virtual void installResource() { m_collection->module()->installPackage(m_tarName); }
56 };
57 */
58 
59 class KoScriptManagerView : public Kross::ActionCollectionView
60 {
61 public:
KoScriptManagerView(KoScriptManagerCollection * collection)62     KoScriptManagerView(KoScriptManagerCollection *collection) : Kross::ActionCollectionView(collection)
63     {
64         setDragEnabled(true);
65         setAcceptDrops(true);
66 
67         Kross::ActionCollectionModel::Mode modelmode = Kross::ActionCollectionModel::Mode(Kross::ActionCollectionModel::Icons | Kross::ActionCollectionModel::ToolTips | Kross::ActionCollectionModel::UserCheckable);
68         Kross::ActionCollectionModel *model = new Kross::ActionCollectionModel(this, Kross::Manager::self().actionCollection(), modelmode);
69         setModel(model);
70         //selectionModel();
71     }
72 
~KoScriptManagerView()73     ~KoScriptManagerView() override {}
74 
model() const75     Kross::ActionCollectionModel *model() const
76     {
77             return static_cast<Kross::ActionCollectionModel*>(Kross::ActionCollectionView::model());
78     }
79 
slotAdd()80     void slotAdd() override
81     {
82         Kross::ActionCollection *collection = model()->rootCollection();
83         if (itemSelection().count() == 1) {
84             collection = Kross::ActionCollectionModel::collection(itemSelection().indexes().first());
85         }
86         KoScriptManagerAddWizard wizard(this, collection);
87         int result = wizard.exec();
88         Q_UNUSED(result);
89     }
slotRemove()90     void slotRemove() override
91     {
92         if (itemSelection().isEmpty()) {
93             return;
94         }
95         // get the actions/collections, indexes will change when things are removed
96         QList<Kross::Action*> actions;
97         QList<Kross::ActionCollection*> collections;
98         foreach (const QModelIndex &idx, itemSelection().indexes()) {
99             Kross::Action *a =  model()->action(idx);
100             if (a) {
101                 actions << a;
102             } else {
103                 Kross::ActionCollection *c = model()->collection(idx);
104                 if (c) {
105                     collections << c;
106                 }
107             }
108         }
109         foreach (Kross::Action *action, actions) {
110             QModelIndex idx = model()->indexForAction(action);
111             if (idx.isValid()) {
112                 //debugKoKross<<"action:"<<action->name();
113                 delete action;
114             }
115         }
116         foreach (Kross::ActionCollection *collection, collections) {
117             QModelIndex idx = model()->indexForCollection(collection);
118             if (idx.isValid()) {
119                 //debugKoKross<<"collection:"<<collection->name();
120                 delete collection;
121             }
122         }
123     }
124 };
125 
KoScriptManagerCollection(QWidget * parent)126 KoScriptManagerCollection::KoScriptManagerCollection(QWidget *parent)
127     : QWidget(parent),
128     m_modified(false)
129 {
130     QHBoxLayout *mainlayout = new QHBoxLayout();
131     mainlayout->setMargin(0);
132     setLayout(mainlayout);
133 
134     m_view = new KoScriptManagerView(this);
135     mainlayout->addWidget(m_view);
136 
137     QWidget *btnwidget = new QWidget(this);
138     QVBoxLayout *btnlayout = new QVBoxLayout();
139     btnlayout->setMargin(0);
140     btnwidget->setLayout(btnlayout);
141     mainlayout->addWidget(btnwidget);
142 
143     //KActionCollection *collection = m_view->actionCollection();
144 
145     m_view->createButton(btnwidget, "run");
146     m_view->createButton(btnwidget, "stop");
147 
148     QFrame *hr1 = new QFrame(btnwidget);
149     hr1->setFrameStyle(QFrame::HLine | QFrame::Sunken);
150     btnlayout->addWidget(hr1, 0);
151 
152     m_view->createButton(btnwidget, "edit");
153     m_view->createButton(btnwidget, "add");
154     m_view->createButton(btnwidget, "remove");
155 
156     btnlayout->addStretch(1);
157     m_view->expandAll();
158 }
159 
~KoScriptManagerCollection()160 KoScriptManagerCollection::~KoScriptManagerCollection()
161 {
162 }
163 
164 /*
165 bool KoScriptManagerCollection::isModified() const
166 {
167     return d->modified;
168 }
169 */
170 
171 #if 0
172 bool KoScriptManagerCollection::slotInstall() {
173     KFileDialog* filedialog = new KFileDialog(
174         QUrl("kfiledialog:///KrossInstallPackage"), // startdir
175         "*.tar.gz *.tgz *.bz2", // filter
176         0, // custom widget
177         0 // parent
178     );
179     filedialog->setCaption(i18n("Install Script Package"));
180     return filedialog->exec() ? module()->installPackage(filedialog->selectedUrl().path()) : false;
181 }
182 void KoScriptManagerView::slotUninstall() {
183     foreach(QModelIndex index, d->selectionmodel->selectedIndexes())
184         if(index.isValid())
185             if(! uninstallPackage( static_cast< Action* >(index.internalPointer()) ))
186                 break;
187 }
188 void KoScriptManagerView::slotNewScripts() {
189     const QString appname = KApplication::kApplication()->objectName();
190     const QString type = QString("%1/script").arg(appname);
191     krossdebug( QString("ScriptManagerView::slotNewScripts %1").arg(type) );
192     if(! d->newstuff) {
193         d->newstuff = new KoScriptManagerNewStuff(this, type);
194         connect(d->newstuff, SIGNAL(installFinished()), this, SLOT(slotNewScriptsInstallFinished()));
195     }
196     KNS::Engine *engine = new KNS::Engine(d->newstuff, type, this);
197     KNS::DownloadDialog *d = new KNS::DownloadDialog(engine, this);
198     d->setCategory(type);
199     KNS::ProviderLoader *p = new KNS::ProviderLoader(this);
200     QObject::connect(p, SIGNAL(providersLoaded(Provider::List*)), d, SLOT(slotProviders(Provider::List*)));
201     p->load(type, QString("http://download.kde.org/khotnewstuff/%1scripts-providers.xml").arg(appname));
202     d->exec();
203 }
204 void KoScriptManagerView::slotNewScriptsInstallFinished() {
205     // Delete KNewStuff's configuration entries. These entries reflect what has
206     // already been installed. As we cannot yet keep them in sync after uninstalling
207     // scripts, we deactivate the check marks entirely.
208     KGlobal::config()->deleteGroup("KNewStuffStatus");
209 }
210 #endif
211 
212 #if 0
213 bool KoScriptManagerModule::installPackage(const QString& scriptpackagefile)
214 {
215     KTar archive( scriptpackagefile );
216     if(! archive.open(QIODevice::ReadOnly)) {
217         KMessageBox::sorry(0, i18n("Could not read the package \"%1\".", scriptpackagefile));
218         return false;
219     }
220 
221     const KArchiveDirectory* archivedir = archive.directory();
222     const KArchiveEntry* entry = archivedir->entry("install.rc");
223     if(! entry || ! entry->isFile()) {
224         KMessageBox::sorry(0, i18n("The package \"%1\" does not contain a valid install.rc file.", scriptpackagefile));
225         return false;
226     }
227 
228     QString xml = static_cast< const KArchiveFile* >(entry)->data();
229     QDomDocument domdoc;
230     if(! domdoc.setContent(xml)) {
231         KMessageBox::sorry(0, i18n("Failed to parse the install.rc file at package \"%1\".", scriptpackagefile));
232         return false;
233     }
234 
235     QString destination = KGlobal::dirs()->saveLocation("appdata", "scripts/", true);
236     if(destination.isNull()) {
237         KMessageBox::sorry(0, i18n("Failed to determinate location where the package \"%1\" should be installed to.", scriptpackagefile));
238         return false;
239     }
240 
241     QString packagename = QFileInfo(scriptpackagefile).baseName();
242     destination += packagename; // add the packagename to the name of the destination-directory.
243 
244     QDir packagepath(destination);
245     if( packagepath.exists() ) {
246         if( KMessageBox::warningContinueCancel(0,
247             i18n("A script package with the name \"%1\" already exists. Replace this package?", packagename),
248             i18n("Replace")) != KMessageBox::Continue )
249                 return false;
250         if(! KIO::NetAccess::del(destination, 0) ) {
251             KMessageBox::sorry(0, i18n("Could not uninstall this script package. You may not have sufficient permissions to delete the folder \"%1\".", destination));
252             return false;
253         }
254     }
255 
256     krossdebug( QString("Copy script-package to destination directory: %1").arg(destination) );
257     archivedir->copyTo(destination, true);
258 
259     QDomNodeList nodelist = domdoc.elementsByTagName("ScriptAction");
260     int nodelistcount = nodelist.count();
261     for(int i = 0; i < nodelistcount; ++i) {
262         QDomElement element = nodelist.item(i).toElement();
263         const QString name = element.attribute("name");
264         Action* action = new Action(Manager::self().actionCollection(), name, packagepath);
265         action->fromDomElement(element);
266         //connect(action, SIGNAL(failed(QString,QString)), this, SLOT(executionFailed(QString,QString)));
267         //connect(action, SIGNAL(success()), this, SLOT(executionSuccessful()));
268         //connect(action, SIGNAL(activated(Kross::Action*)), SIGNAL(executionStarted(Kross::Action*)));
269     }
270 
271     //d->modified = true;
272     return true;
273 }
274 
275 bool KoScriptManagerModule::uninstallPackage(Action* action)
276 {
277     const QString name = action->objectName();
278     QString file = action->file();
279     QFileInfo fi(file);
280     if(file.isNull() || ! fi.exists()) {
281         KMessageBox::sorry(0, i18n("Could not uninstall the script package \"%1\" since the script is not installed.",action->objectName()));
282         return false;
283     }
284     const QString scriptpackagepath = fi.absolutePath();
285     krossdebug( QString("Uninstall script-package with destination directory: %1").arg(scriptpackagepath) );
286     if(! KIO::NetAccess::del(scriptpackagepath, 0) ) {
287         KMessageBox::sorry(0, i18n("Could not uninstall the script package \"%1\". You may not have sufficient permissions to delete the folder \"%1\".",action->objectName()).arg(scriptpackagepath));
288         return false;
289     }
290     delete action; action = 0; // removes the action from d->actions as well
291     d->modified = true;
292     return true;
293 }
294 #endif
295 
KoScriptManagerDialog()296 KoScriptManagerDialog::KoScriptManagerDialog()
297     : KoDialog()
298 {
299     setCaption(i18n("Script Manager"));
300     setButtons(KoDialog::Ok | KoDialog::Cancel);
301     setButtonText(KoDialog::Ok, i18n("Save"));
302     setButtonIcon(KoDialog::Ok, koIcon("document-save"));
303     m_collection = new KoScriptManagerCollection(mainWidget());
304     setMainWidget(m_collection);
305     resize(QSize(520, 380).expandedTo(minimumSizeHint()));
306     connect(this, SIGNAL(accepted()), this, SLOT(slotAccepted()));
307 }
308 
~KoScriptManagerDialog()309 KoScriptManagerDialog::~KoScriptManagerDialog()
310 {
311 }
312 
slotAccepted()313 void KoScriptManagerDialog::slotAccepted()
314 {
315     const QString dir = QStandardPaths::writableLocation(QStandardPaths::DataLocation);
316     if (! dir.isEmpty()) {
317         const QString scriptsDir = dir + QStringLiteral("/scripts/");
318         QDir().mkpath(scriptsDir);
319 
320         const QString file = QFileInfo(scriptsDir, "scripts.rc").absoluteFilePath();
321         QFile f(file);
322         if (f.open(QIODevice::WriteOnly))
323             if (Kross::Manager::self().actionCollection()->writeXml(&f))
324                 debugKoKross<<"Successfully saved file:"<<file;
325         f.close();
326     }
327 }
328