Package refactoring as discussed on panel-devel.

* Icon, preview, screenshot and release notes are gone.
* Everything in a package (except metadata) is in the contents/ directory.
* Package::createPackage and Package::installPackage cleanup.
* Added Package::metadata method to retrieve a metadata object.


svn path=/trunk/KDE/kdebase/workspace/libs/plasma/; revision=743935
This commit is contained in:
Paolo Capriotti 2007-12-02 12:04:57 +00:00
parent 4627a3770c
commit 7dbc6b9ef1
9 changed files with 129 additions and 206 deletions

View File

@ -30,6 +30,7 @@
#include <KIO/Job>
#include <KPluginInfo>
#include <KStandardDirs>
#include <KTempDir>
#include <KTemporaryFile>
#include <KZip>
#include <KDebug>
@ -46,20 +47,24 @@ public:
Private(const PackageStructure& st, const QString& p)
: structure(st),
basePath(p),
valid(QFile::exists(basePath))
valid(QFile::exists(basePath)),
metadata(0)
{
if (valid && basePath[basePath.length() - 1] != '/') {
basePath.append('/');
}
structure.addFileDefinition("metadata", "metadata.desktop",
i18n("Package Meta Data File"));
structure.setRequired("metadata", true);
}
~Private()
{
delete metadata;
}
PackageStructure structure;
QString basePath;
bool valid;
PackageMetadata *metadata;
};
Package::Package(const QString& packageRoot, const QString& package,
@ -73,7 +78,6 @@ Package::Package(const QString &packagePath, const PackageStructure &structure)
{
}
Package::~Package()
{
delete d;
@ -86,14 +90,16 @@ bool Package::isValid() const
}
foreach (const char *dir, d->structure.requiredDirectories()) {
if (!QFile::exists(d->basePath + d->structure.path(dir))) {
if (!QFile::exists(d->basePath + "contents/" + d->structure.path(dir))) {
kWarning(505) << "Could not find required directory" << dir;
d->valid = false;
return false;
}
}
foreach (const char *file, d->structure.requiredFiles()) {
if (!QFile::exists(d->basePath + d->structure.path(file))) {
if (!QFile::exists(d->basePath + "contents/" + d->structure.path(file))) {
kWarning(505) << "Could not find required file" << file;
d->valid = false;
return false;
}
@ -114,7 +120,7 @@ QString Package::filePath(const char* fileType, const QString& filename) const
return QString();
}
path.prepend(d->basePath);
path.prepend(d->basePath + "contents/");
if (!filename.isEmpty()) {
path.append("/").append(filename);
@ -143,7 +149,7 @@ QStringList Package::entryList(const char* fileType) const
return QStringList();
}
QDir dir(d->basePath + path);
QDir dir(d->basePath + "contents/" + path);
if (!dir.exists()) {
return QStringList();
@ -152,6 +158,14 @@ QStringList Package::entryList(const char* fileType) const
return dir.entryList(QDir::Files | QDir::Readable);
}
const PackageMetadata *Package::metadata() const
{
if (!d->metadata) {
d->metadata = new PackageMetadata(d->basePath + "metadata.desktop");
}
return d->metadata;
}
//TODO: provide a version of this that allows one to ask for certain types of packages, etc?
// should we be using KService here instead/as well?
QStringList Package::knownPackages(const QString& packageRoot) // static
@ -184,16 +198,19 @@ bool Package::installPackage(const QString& package,
if (!root.exists()) {
KStandardDirs::makeDir(packageRoot);
if (!root.exists()) {
kWarning(505) << "Could not create package root directory:" << packageRoot;
return false;
}
}
if (!QFile::exists(package)) {
kWarning(505) << "No such file:" << package;
return false;
}
KZip archive(package);
if (!archive.open(QIODevice::ReadOnly)) {
kWarning(505) << "Could not open package file:" << package;
return false;
}
@ -201,46 +218,43 @@ bool Package::installPackage(const QString& package,
const KArchiveEntry* metadata = source->entry("metadata.desktop");
if (!metadata) {
kWarning(505) << "No metadata file in package" << package;
return false;
}
QFile f(package);
QString tempdir = packageRoot + '/' + f.fileName();
if (QFile::exists(tempdir)) {
return false;
}
KTempDir tempdir;
source->copyTo(tempdir.name());
source->copyTo(tempdir);
QString metadataPath = tempdir + "/metadata.desktop";
QString metadataPath = tempdir.name() + "metadata.desktop";
if (!QFile::exists(metadataPath)) {
KIO::SimpleJob* job = KIO::file_delete(tempdir);
job->exec();
kWarning(505) << "No metadata file in package" << package;
return false;
}
PackageMetadata meta(metadataPath);
QString targetName = meta.name();
if (targetName.isEmpty()) {
KIO::SimpleJob* job = KIO::file_delete(tempdir);
job->exec();
kWarning(505) << "Package name not specified";
return false;
}
targetName = packageRoot + '/' + targetName;
if (QFile::exists(targetName)) {
KIO::SimpleJob* job = KIO::file_delete(tempdir);
job->exec();
kWarning(505) << targetName << "already exists";
return false;
}
KIO::FileCopyJob* job = KIO::file_move(tempdir, targetName);
KIO::FileCopyJob* job = KIO::file_move(tempdir.name(), targetName, -1,
KIO::HideProgressInfo);
if (!job->exec()) {
KIO::SimpleJob* job = KIO::file_delete(tempdir);
job->exec();
kWarning(505) << "Could not move package to destination:" << targetName;
return false;
}
// no need to remove the temp dir (which has been moved)
tempdir.setAutoRemove(false);
// and now we register it as a service =)
targetName.append("/metadata.desktop");
@ -257,57 +271,35 @@ bool Package::installPackage(const QString& package,
}
service.append(pluginInfo.pluginName()).append(".desktop");
job = KIO::file_copy(targetName, service);
job = KIO::file_copy(targetName, service, -1, KIO::HideProgressInfo);
return job->exec();
}
bool Package::createPackage(const PackageMetadata & metadata,
const QString& source,
const QString& destination)
bool Package::createPackage(const PackageMetadata &metadata,
const QString &source,
const QString &destination,
const QString &icon) // static
{
if (!metadata.isComplete()) {
kWarning(550) << "Metadata file is not complete" ;
kWarning(550) << "Metadata file is not complete";
return false;
}
// write metadata in a temporary file
KTemporaryFile metadataFile;
metadataFile.open();
metadata.write(metadataFile.fileName());
KTemporaryFile releaseNotes;
//We just write the content of the QString containing the metadata in an
//empty temporary file that we will package with the name metadata.desktop
if (releaseNotes.open()) {
QTextStream out(&releaseNotes);
if (!metadata.releaseNotes().isEmpty()) {
out << metadata.releaseNotes();
} else {
out << "NO_RELEASE_NOTES";
}
if (!metadataFile.open()) {
return false;
}
metadata.write(metadataFile.fileName(), icon);
//OK, we've got the temporary file with the metadata in it.
//Now we just need to put everything into a zip archive.
// put everything into a zip archive
KZip creation(destination);
creation.setCompression(KZip::NoCompression);
if (!creation.open(QIODevice::WriteOnly)) {
return false;
}
creation.addLocalFile(metadataFile.fileName(), "metadata.desktop");
creation.addLocalFile(releaseNotes.fileName(), "notes.txt");
if (!metadata.icon().isEmpty()) {
//TODO: just one icon?
creation.addLocalFile(metadata.icon(), "icon.png");
}
if (!metadata.preview().isEmpty()) {
//TODO: just one icon?
creation.addLocalFile(metadata.preview(), "preview.png");
}
creation.addLocalDirectory(source, "contents");
creation.close();
return true;

View File

@ -93,6 +93,11 @@ class PLASMA_EXPORT Package
**/
QStringList entryList(const char* fileType) const;
/**
* @return the package metadata object.
*/
const PackageMetadata *metadata() const;
/**
* Returns a list of all installed packages
*
@ -123,12 +128,13 @@ class PLASMA_EXPORT Package
* @arg metadata description of the package to create
* @arg source path to local directory containing the individual
* files to be added to the package
* @arg destination path to local directory where the package should
* be created
* @arg destination path to the package that should be created
* @arg icon path to the package icon
**/
static bool createPackage(const PackageMetadata &metadata,
const QString &source,
const QString &destination);
const QString &destination,
const QString &icon = QString());
private:
Q_DISABLE_COPY(Package)

View File

@ -30,10 +30,6 @@ namespace Plasma
class PackageMetadata::Private
{
public:
Private()
: screenshot("screenshot.png")
{}
QString name;
QString description;
QString author;
@ -44,10 +40,6 @@ class PackageMetadata::Private
QString mainFile;
QString app;
QString requiredVersion;
QString releaseNotes;
QString icon;
QString screenshot;
QString preview;
QString type;
QString serviceType;
};
@ -78,7 +70,7 @@ bool PackageMetadata::isComplete() const
d->type.isEmpty());
}
void PackageMetadata::write(const QString& filename) const
void PackageMetadata::write(const QString &filename, const QString &icon) const
{
KConfig cfg(filename);
KConfigGroup config(&cfg, "Desktop Entry");
@ -87,8 +79,9 @@ void PackageMetadata::write(const QString& filename) const
//TODO: this will be a problem for localized names?
config.writeEntry("Name", d->name);
config.writeEntry("Description", d->description);
config.writeEntry("Icon", d->icon);
config.writeEntry("X-KDE-Screenshot", d->screenshot);
if (!icon.isNull()) {
config.writeEntry("Icon", icon);
}
config.writeEntry("X-KDE-ServiceTypes", d->serviceType);
config.writeEntry("X-KDE-PluginInfo-Name", d->name);
config.writeEntry("X-KDE-PluginInfo-Author", d->author);
@ -110,8 +103,6 @@ void PackageMetadata::read(const QString& filename)
//TODO: this will be a problem for localized names?
d->name = config.readEntry("X-KDE-PluginInfo-Name", d->name);
d->description = config.readEntry("Description", d->description);
d->icon = config.readEntry("Icon", d->icon);
d->screenshot= config.readEntry("X-KDE-Screenshot", d->screenshot);
d->serviceType = config.readEntry("X-KDE-ServiceTypes", d->serviceType);
d->author = config.readEntry("X-KDE-PluginInfo-Author", d->author);
d->email = config.readEntry("X-KDE-PluginInfo-Email", d->email);
@ -179,26 +170,6 @@ QString PackageMetadata::requiredVersion() const
return d->requiredVersion;
}
QString PackageMetadata::releaseNotes() const
{
return d->releaseNotes;
}
QString PackageMetadata::icon() const
{
return d->icon;
}
QString PackageMetadata::screenshot() const
{
return d->screenshot;
}
QString PackageMetadata::preview() const
{
return d->preview;
}
QString PackageMetadata::type() const
{
return d->type;
@ -248,6 +219,7 @@ void PackageMetadata::setMainFile(const QString &mainFile)
{
d->mainFile = mainFile;
}
void PackageMetadata::setApplication(const QString &application)
{
d->app = application;
@ -258,26 +230,6 @@ void PackageMetadata::setRequiredVersion(const QString &requiredVersion)
d->requiredVersion = requiredVersion;
}
void PackageMetadata::setReleaseNotes(const QString &releaseNotes)
{
d->releaseNotes = releaseNotes;
}
void PackageMetadata::setIcon(const QString &icon)
{
d->icon = icon;
}
void PackageMetadata::setScreenshot(const QString & screenshot)
{
d->screenshot = screenshot;
}
void PackageMetadata::setPreview(const QString& path)
{
d->preview = path;
}
void PackageMetadata::setType(const QString& type)
{
d->type = type;

View File

@ -52,8 +52,9 @@ public:
* @see KPluginInfo
*
* @arg filename path to the file to write to
* @arg icon path to the package icon
**/
void write(const QString& filename) const;
void write(const QString& filename, const QString &icon = QString()) const;
/**
* Reads in metadata from a file, which should be a .desktop
@ -63,7 +64,7 @@ public:
*
* @arg filename path to the file to write to
**/
void read(const QString& filename);
void read(const QString &filename);
QString name() const;
QString description() const;
@ -76,24 +77,6 @@ public:
QString mainFile() const;
QString application() const;
QString requiredVersion() const;
QString releaseNotes() const;
QString filenameToSave() const;
// filename
QString icon() const;
/**
* @return the name of the screenshot file, screenshot.png by default
*/
QString screenshot() const;
/**
* Path to a PNG file containing a preview image.
* This might be a screenshot, for instance.
*
* @return path to a local image file, or QString() if no
* preview is available
**/
QString preview() const;
QString type() const;
@ -108,27 +91,6 @@ public:
void setMainFile(const QString &);
void setApplication(const QString &);
void setRequiredVersion(const QString &);
void setReleaseNotes(const QString &);
void setFilenameToSave(const QString &);
// filename
void setIcon(const QString &);
/**
* Sets the name of the screenshot file.
*
* @param file the name of the file
*/
void setScreenshot(const QString &file);
/**
* Sets the path for the file containing a preview image.
* This might be a screenshot, for instance.
* A PNG is expected.
*
* @args path path to the preview image file
**/
void setPreview(const QString & path);
void setType(const QString& type);
private:

View File

@ -2,14 +2,10 @@ This file enumerates which classes and methods needs test. Please feel free to
add a specific test you'd like to see for a class/method.
// Finished (as in has test for each method)
package
packagemetadata
packagestructure
// Work in progress. Enumerated methods don't have tests yet.
Package:
- knownPackages
- installPackage
- createPackage
// No tests written atm.
abstractrunner
animator
@ -23,8 +19,6 @@ datacontainer_p
dataengine
dataenginemanager
glapplet
package
packagemetadata
packages_p
phase
plasma_export

View File

@ -59,12 +59,6 @@ void PackageMetadataTest::removeDir(const QString &subdir)
local.rmpath(subd);
}
void PackageMetadataTest::screenshot()
{
// Defealt value
QCOMPARE(pm->screenshot(), QString("screenshot.png"));
}
void PackageMetadataTest::read()
{
pm->read("packagemetadatatest.desktop");
@ -82,8 +76,6 @@ void PackageMetadataTest::read()
QCOMPARE(pm->mainFile(), QString("Main file"));
QCOMPARE(pm->application(), QString("A Test name"));
QCOMPARE(pm->requiredVersion(), QString("1.2.3"));
QCOMPARE(pm->icon(), QString("test"));
QCOMPARE(pm->screenshot(), QString("a_not_default_file.svg"));
QCOMPARE(pm->type(), QString("System test"));
}
@ -100,7 +92,6 @@ void PackageMetadataTest::write()
pm->setMainFile(QString("Main file"));
pm->setApplication(QString("A Test name"));
pm->setRequiredVersion(QString("1.2.3"));
pm->setIcon(QString("test"));
pm->write("somefile.desktop");
delete pm;
@ -118,21 +109,6 @@ void PackageMetadataTest::write()
QCOMPARE(pm->mainFile(), QString("Main file"));
QCOMPARE(pm->application(), QString("A Test name"));
QCOMPARE(pm->requiredVersion(), QString("1.2.3"));
QCOMPARE(pm->icon(), QString("test"));
}
void PackageMetadataTest::preview()
{
QCOMPARE(pm->preview(), QString());
pm->setPreview("Test");
QCOMPARE(pm->preview(), QString("Test"));
}
void PackageMetadataTest::releaseNotes()
{
QCOMPARE(pm->releaseNotes(), QString());
pm->setReleaseNotes("Test");
QCOMPARE(pm->releaseNotes(), QString("Test"));
}
QTEST_KDEMAIN(PackageMetadataTest, NoGUI)

View File

@ -32,11 +32,8 @@ public Q_SLOTS:
void cleanup();
private Q_SLOTS:
void screenshot();
void read();
void write();
void preview();
void releaseNotes();
private:
void removeDir(const QString &subdir);

View File

@ -21,6 +21,9 @@
#include <QDir>
#include <QFile>
#include <KZip>
#include "plasma/packagemetadata.h"
#include "plasma/packages.cpp"
@ -45,8 +48,9 @@ void PlasmoidPackageTest::cleanup()
QDir local = QDir::homePath() + QLatin1String("/.kde-unit-test/packageRoot/");
foreach(const QString &dir, local.entryList(QDir::Dirs))
{
removeDir(QLatin1String("packageRoot/" + dir.toLatin1() + "/code"));
removeDir(QLatin1String("packageRoot/" + dir.toLatin1() + "/images"));
removeDir(QLatin1String("packageRoot/" + dir.toLatin1() + "/contents/code"));
removeDir(QLatin1String("packageRoot/" + dir.toLatin1() + "/contents/images"));
removeDir(QLatin1String("packageRoot/" + dir.toLatin1() + "/contents"));
removeDir(QLatin1String("packageRoot/" + dir.toLatin1()));
}
@ -92,10 +96,10 @@ void PlasmoidPackageTest::createTestPackage(const QString &packageName)
file.close();
// Create the code dir.
QVERIFY(QDir().mkpath(mPackageRoot + "/" + packageName + "/code"));
QVERIFY(QDir().mkpath(mPackageRoot + "/" + packageName + "/contents/code"));
// Create the main file.
file.setFileName(mPackageRoot + "/" + packageName + "/code/main");
file.setFileName(mPackageRoot + "/" + packageName + "/contents/code/main");
QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text));
out << "THIS IS A PLASMOID SCRIPT.....";
@ -106,8 +110,8 @@ void PlasmoidPackageTest::createTestPackage(const QString &packageName)
// files to it for test purposes.
// Create the images dir.
QVERIFY(QDir().mkpath(mPackageRoot + "/" + packageName + "/images"));
file.setFileName(mPackageRoot + "/" + packageName + "/images/image-1.svg");
QVERIFY(QDir().mkpath(mPackageRoot + "/" + packageName + "/contents/images"));
file.setFileName(mPackageRoot + "/" + packageName + "/contents/images/image-1.svg");
QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text));
@ -115,7 +119,7 @@ void PlasmoidPackageTest::createTestPackage(const QString &packageName)
file.flush();
file.close();
file.setFileName(mPackageRoot + "/" + packageName + "/images/image-2.svg");
file.setFileName(mPackageRoot + "/" + packageName + "/contents/images/image-2.svg");
QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text));
@ -148,12 +152,14 @@ void PlasmoidPackageTest::isValid()
QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text));
QTextStream out(&file);
out << "This is a metadatafile";
out << "[Desktop Entry]\n";
out << "Name=test\n";
out << "Description=Just a test desktop file";
file.flush();
file.close();
// Create the code dir.
QVERIFY(QDir().mkpath(mPackageRoot + "/" + mPackage + "/code"));
QVERIFY(QDir().mkpath(mPackageRoot + "/" + mPackage + "/contents/code"));
// No main file yet so should still be invalid.
delete p;
@ -161,7 +167,7 @@ void PlasmoidPackageTest::isValid()
QVERIFY(!p->isValid());
// Create the main file.
file.setFileName(mPackageRoot + "/" + mPackage + "/code/main");
file.setFileName(mPackageRoot + "/" + mPackage + "/contents/code/main");
QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text));
out.setDevice(&file);
@ -184,8 +190,8 @@ void PlasmoidPackageTest::filePath()
QCOMPARE(p->filePath("scripts", "main"), QString());
QVERIFY(QDir().mkpath(mPackageRoot + "/" + mPackage + "/code"));
QFile file(mPackageRoot + "/" + mPackage + "/code/main");
QVERIFY(QDir().mkpath(mPackageRoot + "/" + mPackage + "/contents/code"));
QFile file(mPackageRoot + "/" + mPackage + "/contents/code/main");
QVERIFY(file.open(QIODevice::WriteOnly | QIODevice::Text));
QTextStream out(&file);
@ -197,7 +203,7 @@ void PlasmoidPackageTest::filePath()
delete p;
p = new Plasma::Package(mPackageRoot, mPackage, *ps);
QString path = mPackageRoot + "/" + mPackage + "/code/main";
QString path = mPackageRoot + "/" + mPackage + "/contents/code/main";
// Two ways to get the same info.
// 1. Give the file type which refers to a class of files (a directory) in
@ -285,7 +291,44 @@ void PlasmoidPackageTest::metadata()
QString path = mPackageRoot + '/' + plasmoid + "/metadata.desktop";
p = new Plasma::Package(mPackageRoot, plasmoid, *ps);
QCOMPARE(p->filePath("metadata"), path);
const Plasma::PackageMetadata *metadata = p->metadata();
QVERIFY(metadata);
QCOMPARE(metadata->name(), plasmoid);
}
void PlasmoidPackageTest::createAndInstallPackage()
{
QString plasmoid("plasmoid_to_package");
createTestPackage(plasmoid);
QString packagePath = mPackageRoot + '/' + "package.zip";
Plasma::PackageMetadata metadata(
QString(KDESRCDIR) + "/packagemetadatatest.desktop");
QVERIFY(Plasma::Package::createPackage(metadata,
mPackageRoot + '/' + plasmoid + "/contents",
packagePath));
QVERIFY(QFile::exists(packagePath));
KZip package(packagePath);
QVERIFY(package.open(QIODevice::ReadOnly));
const KArchiveDirectory *dir = package.directory();
QVERIFY(dir);
QVERIFY(dir->entry("metadata.desktop"));
const KArchiveEntry *contentsEntry = dir->entry("contents");
QVERIFY(contentsEntry);
QVERIFY(contentsEntry->isDirectory());
const KArchiveDirectory *contents =
static_cast<const KArchiveDirectory *>(contentsEntry);
QVERIFY(contents->entry("code"));
QVERIFY(contents->entry("images"));
Plasma::Package::installPackage(packagePath, mPackageRoot);
QString installedPackage = mPackageRoot + "/test";
QVERIFY(QFile::exists(installedPackage));
p = new Plasma::Package(installedPackage, *ps);
QVERIFY(p->isValid());
}
QTEST_KDEMAIN(PlasmoidPackageTest, NoGUI)

View File

@ -37,6 +37,7 @@ private Q_SLOTS:
void entryList();
void knownPackages();
void metadata();
void createAndInstallPackage();
private:
void removeDir(const QString &subdir);