2020-05-17 Fred Gleason <fredg@paravelsystems.com>

* Added a 'FEEDS.CHANNEL_IMAGE_ID' field to the database.
	* Added a 'FEEDS.DEFAULT_ITEM_IMAGE_ID' field to the database.
	* Added a 'PODCASTS.ITEM_IMAGE_ID' field to the database.
	* Incremented the database version to 324.
	* Added an 'Image Manager' dialog to rdadmin(1).
	* Added an 'Image Viewer' dialog on rdadmin(1).

Signed-off-by: Fred Gleason <fredg@paravelsystems.com>
This commit is contained in:
Fred Gleason 2020-05-17 10:36:04 -04:00
parent eddf0fa97f
commit 2e9f8b7608
40 changed files with 2231 additions and 76 deletions

View File

@ -19928,3 +19928,10 @@
* Added a 'FEED_IMAGES' table to the database.
* Incremented the database version to 323.
* Added 'feed_image_test' in 'tests/'.
2020-05-17 Fred Gleason <fredg@paravelsystems.com>
* Added a 'FEEDS.CHANNEL_IMAGE_ID' field to the database.
* Added a 'FEEDS.DEFAULT_ITEM_IMAGE_ID' field to the database.
* Added a 'PODCASTS.ITEM_IMAGE_ID' field to the database.
* Incremented the database version to 324.
* Added an 'Image Manager' dialog to rdadmin(1).
* Added an 'Image Viewer' dialog on rdadmin(1).

View File

@ -11,5 +11,6 @@ FEED_KEY_NAME varchar(8) From FEEDS.KEY_NAME
WIDTH int(11) signed Pixels
HEIGHT int(11) signed Pixels
DEPTH int(11) signed Bits/pixel
DESCRIPTION text
DESCRIPTION varchar(191)
FILE_EXTENSION varchar(10)
DATA mediumblob

View File

@ -11,32 +11,34 @@ AUDIENCE_METRICS enum('N','Y')
CHANNEL_TITLE varchar(191)
CHANNEL_DESCRIPTION text
CHANNEL_CATEGORY varchar(64)
CHANNEL_LINK varchar(191)
CHANNEL_COPYRIGHT varchar(64)
CHANNEL_EDITOR varchar(64)
CHANNEL_WEBMASTER varchar(64)
CHANNEL_LANGUAGE varchar(5)
BASE_URL varchar(191)
BASE_PREAMBLE varchar(191)
PURGE_URL varchar(191)
PURGE_USERNAME varchar(64)
PURGE_PASSWORD varchar(64)
RSS_SCHEMA int(10) unsigned From RSS_SCHEMAS.ID
HEADER_XML text
CHANNEL_XML text
ITEM_XML text
CAST_ORDER enum('N','Y')
MAX_SHELF_LIFE int(11)
LAST_BUILD_DATETIME datetime
ORIGIN_DATETIME datetime
ENABLE_AUTOPOST enum('N','Y')
KEEP_METADATA enum('N','Y')
UPLOAD_FORMAT int(11)
UPLOAD_CHANNELS int(11)
UPLOAD_SAMPRATE int(11)
UPLOAD_BITRATE int(11)
UPLOAD_QUALITY int(11)
UPLOAD_EXTENSION varchar(16)
NORMALIZE_LEVEL int(11)
REDIRECT_PATH varchar(191)
MEDIA_LINK_MODE int(11)
CHANNEL_LINK varchar(191)
CHANNEL_COPYRIGHT varchar(64)
CHANNEL_EDITOR varchar(64)
CHANNEL_WEBMASTER varchar(64)
CHANNEL_LANGUAGE varchar(5)
CHANNEL_IMAGE_ID int(11) From FEED_IMAGES.ID
BASE_URL varchar(191)
BASE_PREAMBLE varchar(191)
PURGE_URL varchar(191)
PURGE_USERNAME varchar(64)
PURGE_PASSWORD varchar(64)
RSS_SCHEMA int(10) unsigned From RSS_SCHEMAS.ID
HEADER_XML text
CHANNEL_XML text
ITEM_XML text
CAST_ORDER enum('N','Y')
MAX_SHELF_LIFE int(11)
LAST_BUILD_DATETIME datetime
ORIGIN_DATETIME datetime
ENABLE_AUTOPOST enum('N','Y')
KEEP_METADATA enum('N','Y')
DEFAULT_ITEM_IMAGE_ID int(11) From FEED_IMAGES.ID
UPLOAD_FORMAT int(11)
UPLOAD_CHANNELS int(11)
UPLOAD_SAMPRATE int(11)
UPLOAD_BITRATE int(11)
UPLOAD_QUALITY int(11)
UPLOAD_EXTENSION varchar(16)
NORMALIZE_LEVEL int(11)
REDIRECT_PATH varchar(191)
MEDIA_LINK_MODE int(11)

View File

@ -15,6 +15,7 @@ ITEM_COMMENTS varchar(191)
ITEM_AUTHOR varchar(191)
ITEM_SOURCE_TEXT varchar(64)
ITEM_SOURCE_URL varchar(191)
ITEM_IMAGE_ID int(11) From FEED_IMAGES.ID
AUDIO_FILENAME varchar(191)
AUDIO_LENGTH int(10) unsigned
AUDIO_TIME int(10) unsigned

View File

@ -143,6 +143,8 @@ dist_librd_la_SOURCES = dbversion.h\
rdhotkeys.cpp rdhotkeys.h\
rdhotkeylist.cpp rdhotkeylist.h\
rdidvalidator.cpp rdidvalidator.h\
rdimagepickerbox.cpp rdimagepickerbox.h\
rdimagepickermodel.cpp rdimagepickermodel.h\
rdimport_audio.cpp rdimport_audio.h\
rdinstancelock.cpp rdinstancelock.h\
rd.h\
@ -291,6 +293,8 @@ nodist_librd_la_SOURCES = moc_rdadd_cart.cpp\
moc_rdgpioselector.cpp\
moc_rdhotkeys.cpp\
moc_rdhotkeylist.cpp\
moc_rdimagepickerbox.cpp\
moc_rdimagepickermodel.cpp\
moc_rdimport_audio.cpp\
moc_rdkernelgpio.cpp\
moc_rdlineedit.cpp\

View File

@ -24,7 +24,7 @@
/*
* Current Database Version
*/
#define RD_VERSION_DATABASE 323
#define RD_VERSION_DATABASE 324
#endif // DBVERSION_H

View File

@ -102,6 +102,8 @@ SOURCES += rdgroup.cpp
SOURCES += rdgroup_list.cpp
SOURCES += rdhash.cpp
SOURCES += rdidvalidator.cpp
SOURCES += rdimagepickerbox.cpp
SOURCES += rdimagepickermodel.cpp
SOURCES += rdimport_audio.cpp
SOURCES += rdkernelgpio.cpp
SOURCES += rdlibrary_conf.cpp
@ -239,6 +241,8 @@ HEADERS += rdgroup_list.h
HEADERS += rdgroup.h
HEADERS += rdhash.h
HEADERS += rdidvalidator.h
HEADERS += rdimagepickerbox.h
HEADERS += rdimagepickermodel.h
HEADERS += rdimport_audio.h
HEADERS += rdkernelgpio.h
HEADERS += rdlibrary_conf.h

View File

@ -2007,6 +2007,13 @@ pro dobu zprávy.
<translation>PIN:</translation>
</message>
</context>
<context>
<name>RDImagePickerModel</name>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RDImportAudio</name>
<message>

View File

@ -1998,6 +1998,13 @@ figure for the report period.
<translation>PIN:</translation>
</message>
</context>
<context>
<name>RDImagePickerModel</name>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RDImportAudio</name>
<message>

View File

@ -1996,6 +1996,13 @@ para el período a reportar.
<translation>Pin:</translation>
</message>
</context>
<context>
<name>RDImagePickerModel</name>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RDImportAudio</name>
<message>

View File

@ -1753,6 +1753,13 @@ figure for the report period.
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RDImagePickerModel</name>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RDImportAudio</name>
<message>

View File

@ -1996,6 +1996,13 @@ for rapportperioden.
<translation>Pin-nummer:</translation>
</message>
</context>
<context>
<name>RDImagePickerModel</name>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RDImportAudio</name>
<message>

View File

@ -1996,6 +1996,13 @@ for rapportperioden.
<translation>Pin-nummer:</translation>
</message>
</context>
<context>
<name>RDImagePickerModel</name>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RDImportAudio</name>
<message>

View File

@ -2002,6 +2002,13 @@ para o período do relatório
<translation>Pin:</translation>
</message>
</context>
<context>
<name>RDImagePickerModel</name>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>RDImportAudio</name>
<message>

View File

@ -448,6 +448,11 @@
*/
#define RD_IMAGE_FILE_FILTER "Image Files (*.png *.bmp *.xbm *.xpm *.pbm *.pgm *.ppm *.jpg *.mng *.gif *.PNG *.BMP *.XBM *.XPM *.PBM *.PGM *.PPM *.JPG *.MNG *.GIF)\nAll Files (*.*)"
/*
* Podcast Image File Filter for QFileDialog
*/
#define RD_PODCAST_IMAGE_FILE_FILTER "Image Files (*.png *.jpg *.PNG *.JPG *.jpeg *.JPEG)\nAll Files (*.*)"
/*
* Loadable Module Filter for QFileDialog
*/

View File

@ -117,6 +117,7 @@ RDDelete::ErrorCode RDDelete::runDelete(const QString &username,
rda->syslog(LOG_ERR,"unable to initialize curl library\n");
return RDDelete::ErrorInternal;
}
curl_easy_setopt(curl,CURLOPT_URL,conv_target_url.toEncoded().constData());
curl_easy_setopt(curl,CURLOPT_USERNAME,username.toUtf8().constData());
curl_easy_setopt(curl,CURLOPT_PASSWORD,password.toUtf8().constData());
@ -150,8 +151,6 @@ RDDelete::ErrorCode RDDelete::runDelete(const QString &username,
curl_easy_setopt(curl,CURLOPT_QUOTE,cmds);
switch((err=curl_easy_perform(curl))) {
case CURLE_OK:
case CURLE_REMOTE_ACCESS_DENIED: // Sometimes we get this even when
@ -191,7 +190,7 @@ RDDelete::ErrorCode RDDelete::runDelete(const QString &username,
}
curl_slist_free_all(cmds);
curl_easy_cleanup(curl);
return ret;
}

View File

@ -18,6 +18,7 @@
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include <errno.h>
#include <math.h>
#include <curl/curl.h>
@ -280,6 +281,32 @@ void RDFeed::setChannelLanguage(const QString &str)
}
int RDFeed::channelImageId() const
{
return RDGetSqlValue("FEEDS","KEY_NAME",feed_keyname,"CHANNEL_IMAGE_ID").
toInt();
}
void RDFeed::setChannelImageId(int img_id) const
{
SetRow("CHANNEL_IMAGE_ID",img_id);
}
int RDFeed::defaultItemImageId() const
{
return RDGetSqlValue("FEEDS","KEY_NAME",feed_keyname,"DEFAULT_ITEM_IMAGE_ID").
toInt();
}
void RDFeed::setDefaultItemImageId(int img_id) const
{
SetRow("DEFAULT_ITEM_IMAGE_ID",img_id);
}
QString RDFeed::baseUrl(const QString &subfeed_key_name) const
{
QString key_name=subfeed_key_name;
@ -625,6 +652,94 @@ void RDFeed::setMediaLinkMode(RDFeed::MediaLinkMode mode) const
}
int RDFeed::importImageFile(const QString &pathname,QString *err_msg,
QString desc) const
{
bool ok=false;
QString sql;
int ret;
*err_msg="OK";
//
// Load the image
//
QFile file(pathname);
if(!file.open(QIODevice::ReadOnly)) {
*err_msg=QString("unable to open image file [")+
QString(strerror(errno))+"]";
return -1;
}
QByteArray data=file.readAll();
file.close();
//
// Validate the image
//
QImage *img=new QImage();
if(!img->loadFromData(data)) {
*err_msg="invalid image file";
return -1;
}
//
// Fix up the Description
//
if(desc.isEmpty()) {
desc=tr("Imported from")+" "+pathname;
}
//
// FIXME: Upload to remote file store here...
//
//
// Write it to the DB
//
QStringList f0=pathname.split(".",QString::SkipEmptyParts);
sql=QString("insert into FEED_IMAGES set ")+
QString().sprintf("FEED_ID=%u,",id())+
"FEED_KEY_NAME=\""+RDEscapeString(keyName())+"\","+
QString().sprintf("WIDTH=%d,",img->width())+
QString().sprintf("HEIGHT=%d,",img->height())+
QString().sprintf("DEPTH=%d,",img->depth())+
"DESCRIPTION=\""+RDEscapeString(desc)+"\","+
"FILE_EXTENSION=\""+RDEscapeString(f0.last().toLower())+"\","+
"DATA="+RDEscapeBlob(data);
ret=RDSqlQuery::run(sql,&ok).toInt();
if(!ok) {
*err_msg="Unable to write to database";
return -1;
}
return ret;
}
bool RDFeed::deleteImage(int img_id,QString *err_msg)
{
QString sql;
RDSqlQuery *q=NULL;
*err_msg="OK";
//
// FIXME: Delete from remote file store here...
//
sql=QString("delete from FEED_IMAGES where ")+
QString().sprintf("ID=%d",img_id);
if(!RDSqlQuery::apply(sql,err_msg)) {
*err_msg=QString("database error: ")+*err_msg;
delete q;
return false;
}
delete q;
return true;
}
QString RDFeed::audioUrl(RDFeed::MediaLinkMode mode,
const QString &cgi_hostname,unsigned cast_id)
{
@ -654,6 +769,26 @@ QString RDFeed::audioUrl(RDFeed::MediaLinkMode mode,
}
QString RDFeed::imageUrl(int img_id) const
{
QString ret;
QString sql=QString("select ")+
"FEED_ID,"+ // 00
"FILE_EXTENSION "+ // 01
"from FEED_IMAGES where "+
QString().sprintf("ID=%d",img_id);
RDSqlQuery *q=new RDSqlQuery(sql);
if(q->first()) {
ret=baseUrl(q->value(0).toUInt())+"/"+
RDFeed::imageFilename(id(),img_id,q->value(1).toString());
}
delete q;
return ret;
}
bool RDFeed::postXml(QString *err_msg)
{
CURL *curl=NULL;
@ -1417,6 +1552,12 @@ QString RDFeed::rssItemTemplate(RDFeed::RssSchema schema)
}
QString RDFeed::imageFilename(int feed_id,int img_id,const QString &ext)
{
return QString().sprintf("img%06d_%06d.",feed_id,img_id)+ext;
}
unsigned RDFeed::CreateCast(QString *filename,int bytes,int msecs) const
{
QString sql;

View File

@ -66,6 +66,10 @@ class RDFeed : public QObject
void setChannelWebmaster(const QString &str) const;
QString channelLanguage() const;
void setChannelLanguage(const QString &str);
int channelImageId() const;
void setChannelImageId(int img_id) const;
int defaultItemImageId() const;
void setDefaultItemImageId(int img_id) const;
QString baseUrl(const QString &subfeed_key_name) const;
QString baseUrl(int subfeed_feed_id) const;
void setBaseUrl(const QString &str) const;
@ -118,8 +122,12 @@ class RDFeed : public QObject
void setRedirectPath(const QString &str);
RDFeed::MediaLinkMode mediaLinkMode() const;
void setMediaLinkMode(RDFeed::MediaLinkMode mode) const;
int importImageFile(const QString &pathname,QString *err_msg,
QString desc="") const;
bool deleteImage(int img_id,QString *err_msg);
QString audioUrl(RDFeed::MediaLinkMode mode,const QString &cgi_hostname,
unsigned cast_id);
QString imageUrl(int img_id) const;
bool postXml(QString *err_msg);
bool postXmlConditional(const QString &caption,QWidget *widget);
bool deleteXml(QString *err_msg);
@ -137,6 +145,7 @@ class RDFeed : public QObject
static QString rssHeaderTemplate(RssSchema schema);
static QString rssChannelTemplate(RssSchema schema);
static QString rssItemTemplate(RssSchema schema);
static QString imageFilename(int feed_id,int img_id,const QString &ext);
signals:
void postProgressChanged(int step);

81
lib/rdimagepickerbox.cpp Normal file
View File

@ -0,0 +1,81 @@
// rdimagepickerbox.cpp
//
// ComboBox for selecting images
//
// (C) Copyright 2020 Fred Gleason <fredg@paravelsystems.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include "rdimagepickerbox.h"
RDImagePickerBox::RDImagePickerBox(const QString &tbl_name,
const QString &cat_id_col,
const QString &img_id_col,QWidget *parent)
: QComboBox(parent)
{
c_model=new RDImagePickerModel(tbl_name,cat_id_col,img_id_col,this);
setModel(c_model);
setCurrentIndex(0);
}
RDImagePickerBox::~RDImagePickerBox()
{
delete c_model;
}
int RDImagePickerBox::currentImageId() const
{
if(currentIndex()<0) {
return -1;
}
return c_model->imageId(currentIndex());
}
void RDImagePickerBox::setCurrentImageId(int img_id)
{
setCurrentIndex(c_model->imageRowOfId(img_id));
}
void RDImagePickerBox::refresh()
{
int current_id=currentImageId();
c_model->refresh();
setCurrentImageId(current_id);
}
void RDImagePickerBox::setCategoryId(int id)
{
c_model->setCategoryId(id);
setCurrentIndex(0);
}
void RDImagePickerBox::resizeEvent(QResizeEvent *e)
{
int index=currentIndex();
QSize img_size(size().height()-4,size().height()-4);
c_model->rescaleImages(img_size);
setIconSize(img_size);
setCurrentIndex(index);
}

50
lib/rdimagepickerbox.h Normal file
View File

@ -0,0 +1,50 @@
// rdimagepickerbox.h
//
// ComboBox for selecting images
//
// (C) Copyright 2020 Fred Gleason <fredg@paravelsystems.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#ifndef RDIMAGEPICKERBOX_H
#define RDIMAGEPICKERBOX_H
#include <qcombobox.h>
#include <rdimagepickermodel.h>
class RDImagePickerBox : public QComboBox
{
Q_OBJECT;
public:
RDImagePickerBox(const QString &tbl_name,const QString &cat_id_col,
const QString &img_id_col,QWidget *parent=0);
~RDImagePickerBox();
int currentImageId() const;
void refresh();
public slots:
void setCurrentImageId(int img_id);
void setCategoryId(int id);
protected:
void resizeEvent(QResizeEvent *e);
private:
RDImagePickerModel *c_model;
};
#endif // RDIMAGEPICKERBOX_H

205
lib/rdimagepickermodel.cpp Normal file
View File

@ -0,0 +1,205 @@
// rdimagepickermodel.cpp
//
// One-dimensional model for picking images
//
// (C) Copyright 2020 Fred Gleason <fredg@paravelsystems.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include <qimage.h>
#include "rddb.h"
#include "rdimagepickermodel.h"
RDImagePickerModel::RDImagePickerModel(const QString &tbl_name,
const QString &cat_id_col,
const QString &img_id_col,
QObject *parent)
: QAbstractListModel(parent)
{
c_table_name=tbl_name;
c_category_column=cat_id_col;
c_image_column=img_id_col;
c_category_id=-1;;
c_image_size=QSize(100,100);
}
RDImagePickerModel::~RDImagePickerModel()
{
for(int i=0;i<c_images.size();i++) {
delete c_images.at(i);
}
c_images.clear();
}
int RDImagePickerModel::categoryId() const
{
return c_category_id;
}
int RDImagePickerModel::imageId(int row) const
{
if(row<0) {
return -1;
}
return c_image_ids.at(row);
}
int RDImagePickerModel::imageRowOfId(int img_id) const
{
if(img_id<0) {
return -1;
}
return c_image_ids.indexOf(img_id);
}
void RDImagePickerModel::rescaleImages(const QSize &size)
{
if(size!=c_image_size) {
LoadRows(c_category_id,size);
c_image_size=size;
}
}
void RDImagePickerModel::update(int row)
{
QString sql;
RDSqlQuery *q=NULL;
sql=QString("select ")+
"DESCRIPTION,"+ // 00
"WIDTH,"+ // 01
"HEIGHT "+ // 02
"from FEED_IMAGES where "+
QString().sprintf("ID=%d",c_image_ids.at(row));
q=new RDSqlQuery(sql);
if(q->first()) {
c_descriptions[row]=q->value(0).toString()+
QString().sprintf("\n [%dx%d]",q->value(1).toInt(),q->value(2).toInt());
emit dataChanged(createIndex(row,0),createIndex(row,0));
}
delete q;
}
void RDImagePickerModel::refresh()
{
LoadRows(c_category_id,c_image_size);
}
int RDImagePickerModel::rowCount(const QModelIndex &parent) const
{
return c_images.size();
}
QVariant RDImagePickerModel::data(const QModelIndex &index,int role) const
{
if(index.column()==0) {
if(role==Qt::DisplayRole) {
return QVariant(c_descriptions.at(index.row()));
}
if(role==Qt::DecorationRole) {
if(c_images.at(index.row())!=NULL) {
return QVariant(*(c_images.at(index.row())));
}
}
if(role==Qt::SizeHintRole) {
return QVariant(QSize(200,50));
}
}
return QVariant();
}
QVariant RDImagePickerModel::headerData(int section,Qt::Orientation orient,
int role)
{
if((orient==Qt::Horizontal)&&(section==0)) {
if(role==Qt::DisplayRole) {
return QVariant(tr("Image"));
}
}
return QVariant();
}
void RDImagePickerModel::setCategoryId(int id)
{
if(id!=c_category_id) {
LoadRows(id,c_image_size);
c_category_id=id;
}
}
void RDImagePickerModel::LoadRows(int cat_id,const QSize &img_size)
{
QString sql;
RDSqlQuery *q=NULL;
QImage img;
//
// Clear stale data
//
if(c_images.size()>0) {
beginRemoveRows(QModelIndex(),0,c_images.size()-1);
for(int i=0;i<c_images.size();i++) {
delete c_images.at(i);
}
c_images.clear();
c_descriptions.clear();
c_image_ids.clear();
endRemoveRows();
}
//
// Load new data
//
sql=QString("select ")+
"ID,"+ // 00
"DESCRIPTION,"+ // 01
"WIDTH,"+ // 02
"HEIGHT,"+ // 03
"DATA "+ // 04
"from "+c_table_name+" where "+
c_category_column+QString().sprintf("=%d ",cat_id)+
"order by DESCRIPTION";
q=new RDSqlQuery(sql);
if(q->size()>0) {
beginInsertRows(QModelIndex(),0,q->size()-1);
while(q->next()) {
c_image_ids.push_back(q->value(0).toUInt());
c_descriptions.
push_back(q->value(1).toString()+
QString().sprintf("\n [%dx%d]",
q->value(2).toInt(),q->value(3).toInt()));
img.loadFromData(q->value(4).toByteArray());
c_images.push_back(new QPixmap(QPixmap::fromImage(img.scaled(img_size,
Qt::KeepAspectRatio,Qt::SmoothTransformation))));
}
endInsertRows();
}
delete q;
}

64
lib/rdimagepickermodel.h Normal file
View File

@ -0,0 +1,64 @@
// rdimagepickermodel.h
//
// One-dimensional model for picking images
//
// (C) Copyright 2020 Fred Gleason <fredg@paravelsystems.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#ifndef RDIMAGEPICKERMODEL_H
#define RDIMAGEPICKERMODEL_H
#include <QAbstractListModel>
#include <qlist.h>
#include <qpixmap.h>
#include <qstringlist.h>
class RDImagePickerModel : public QAbstractListModel
{
Q_OBJECT;
public:
RDImagePickerModel(const QString &tbl_name,const QString &cat_id_col,
const QString &img_id_col,QObject *parent=0);
~RDImagePickerModel();
int categoryId() const;
int imageId(int row) const;
int imageRowOfId(int img_id) const;
void rescaleImages(const QSize &size);
void update(int row);
void refresh();
int rowCount(const QModelIndex &parent=QModelIndex()) const;
QVariant data(const QModelIndex &index,int role=Qt::DisplayRole) const;
QVariant headerData(int section,Qt::Orientation orient,
int role=Qt::DisplayRole);
public slots:
void setCategoryId(int id);
private:
void LoadRows(int cat_id,const QSize &img_size);
QString c_table_name;
QString c_category_column;
QString c_image_column;
int c_category_id;
QSize c_image_size;
QList<QPixmap *> c_images;
QStringList c_descriptions;
QList<int> c_image_ids;
};
#endif // RDIMAGEPICKERMODEL_H

View File

@ -72,6 +72,7 @@ dist_rdadmin_SOURCES = add_feed.cpp add_feed.h\
edit_group.cpp edit_group.h\
edit_hostvar.cpp edit_hostvar.h\
edit_hotkeys.cpp edit_hotkeys.h\
edit_image.cpp edit_image.h\
edit_jack.cpp edit_jack.h\
edit_jack_client.cpp edit_jack_client.h\
edit_livewiregpio.cpp edit_livewiregpio.h\
@ -107,6 +108,7 @@ dist_rdadmin_SOURCES = add_feed.cpp add_feed.h\
list_gpis.cpp list_gpis.h\
list_groups.cpp list_groups.h\
list_hostvars.cpp list_hostvars.h\
list_images.cpp list_images.h\
list_livewiregpios.cpp list_livewiregpios.h\
list_matrices.cpp list_matrices.h\
list_nodes.cpp list_nodes.h\
@ -153,6 +155,7 @@ nodist_rdadmin_SOURCES = global_credits.c\
moc_edit_group.cpp\
moc_edit_hostvar.cpp\
moc_edit_hotkeys.cpp\
moc_edit_image.cpp\
moc_edit_jack.cpp\
moc_edit_jack_client.cpp\
moc_edit_livewiregpio.cpp\
@ -187,6 +190,7 @@ nodist_rdadmin_SOURCES = global_credits.c\
moc_list_gpis.cpp\
moc_list_groups.cpp\
moc_list_hostvars.cpp\
moc_list_images.cpp\
moc_list_livewiregpios.cpp\
moc_list_matrices.cpp\
moc_list_nodes.cpp\

View File

@ -49,9 +49,16 @@ EditFeed::EditFeed(const QString &feed,QWidget *parent)
setMaximumSize(sizeHint());
feed_feed=new RDFeed(feed,rda->config(),this);
feed_image_model=new RDImagePickerModel("FEED_IMAGES","FEED_ID","ID",this);
feed_image_model->setCategoryId(feed_feed->id());
setWindowTitle("RDAdmin - "+tr("Feed: ")+feed);
//
// Dialogs
//
feed_images_dialog=new ListImages(feed_image_model,this);
//
// Feed Name
//
@ -75,6 +82,17 @@ EditFeed::EditFeed(const QString &feed,QWidget *parent)
connect(feed_is_superfeed_button,SIGNAL(clicked()),
this,SLOT(selectSubfeedsData()));
//
// Image Management Button
//
feed_list_images_button=new QPushButton(tr("Manage")+"\n"+tr("Images"),this);
feed_list_images_button->setFont(buttonFont());
connect(feed_list_images_button,SIGNAL(clicked()),
this,SLOT(listImagesData()));
//
// Audience Metrics
//
feed_audience_metrics_check=new QCheckBox(this);
feed_audience_metrics_label=new QLabel(tr("Collect Audience Metrics"),this);
feed_audience_metrics_label->setFont(labelFont());
@ -160,6 +178,17 @@ EditFeed::EditFeed(const QString &feed,QWidget *parent)
feed_channel_language_label->setFont(labelFont());
feed_channel_language_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
//
// Channel Image
//
feed_channel_image_box=new
RDImagePickerBox("FEED_IMAGES","FEED_ID","ID",this);
feed_channel_image_box->setCategoryId(feed_feed->id());
feed_channel_image_label=
new QLabel(feed_channel_image_box,tr("Image")+":",this);
feed_channel_image_label->setFont(labelFont());
feed_channel_image_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
//
// Channel Description
//
@ -328,6 +357,16 @@ EditFeed::EditFeed(const QString &feed,QWidget *parent)
feed_media_link_mode_label->setFont(labelFont());
feed_media_link_mode_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
//
// Default Item Image
//
feed_item_image_box=new RDImagePickerBox("FEED_IMAGES","FEED_ID","ID",this);
feed_item_image_box->setCategoryId(feed_feed->id());
feed_item_image_label=
new QLabel(feed_item_image_box,tr("Default Item Image")+":",this);
feed_item_image_label->setFont(labelFont());
feed_item_image_label->setAlignment(Qt::AlignRight|Qt::AlignVCenter);
//
// Feed Redirection
//
@ -421,6 +460,7 @@ EditFeed::EditFeed(const QString &feed,QWidget *parent)
feed_channel_editor_edit->setText(feed_feed->channelEditor());
feed_channel_webmaster_edit->setText(feed_feed->channelWebmaster());
feed_channel_description_edit->setPlainText(feed_feed->channelDescription());
feed_channel_image_box->setCurrentImageId(feed_feed->channelImageId());
feed_channel_language_edit->setText(feed_feed->channelLanguage());
feed_base_url_edit->setText(feed_feed->baseUrl(""));
feed_base_preamble_edit->setText(feed_feed->basePreamble());
@ -456,6 +496,7 @@ EditFeed::EditFeed(const QString &feed,QWidget *parent)
}
feed_castorder_box->setCurrentItem(feed_feed->castOrder());
feed_media_link_mode_box->setCurrentItem((int)feed_feed->mediaLinkMode());
feed_item_image_box->setCurrentImageId(feed_feed->defaultItemImageId());
feed_redirect_url_edit->setText(feed_feed->redirectPath());
feed_redirect_check->setChecked(!feed_redirect_url_edit->text().isEmpty());
@ -466,9 +507,16 @@ EditFeed::EditFeed(const QString &feed,QWidget *parent)
}
EditFeed::~EditFeed()
{
delete feed_image_model;
delete feed_feed;
}
QSize EditFeed::sizeHint() const
{
return QSize(1000,665);
return QSize(1000,740);
}
@ -532,6 +580,14 @@ void EditFeed::redirectToggledData(bool state)
}
void EditFeed::listImagesData()
{
feed_images_dialog->exec(feed_feed);
feed_channel_image_box->refresh();
feed_item_image_box->refresh();
}
void EditFeed::okData()
{
RDDelete *d=new RDDelete(rda->config(),this);
@ -570,6 +626,8 @@ void EditFeed::okData()
feed_feed->setChannelWebmaster(feed_channel_webmaster_edit->text());
feed_feed->setChannelDescription(feed_channel_description_edit->text());
feed_feed->setChannelLanguage(feed_channel_language_edit->text());
feed_feed->setChannelImageId(feed_channel_image_box->currentImageId());
feed_feed->setDefaultItemImageId(feed_item_image_box->currentImageId());
feed_feed->setBaseUrl(feed_base_url_edit->text());
feed_feed->setBasePreamble(feed_base_preamble_edit->text());
feed_feed->setPurgeUrl(feed_purge_url_edit->text());
@ -618,17 +676,21 @@ void EditFeed::cancelData()
void EditFeed::resizeEvent(QResizeEvent *e)
{
feed_image_model->rescaleImages(QSize(36,36));
feed_keyname_edit->setGeometry(115,11,100,19);
feed_keyname_label->setGeometry(10,11,100,19);
feed_is_superfeed_box->setGeometry(115,33,60,19);
feed_is_superfeed_label->setGeometry(10,33,100,19);
feed_is_superfeed_button->setGeometry(185,33,160,38);
feed_is_superfeed_button->setGeometry(185,33,140,38);
feed_list_images_button->setGeometry(345,33,100,38);
feed_audience_metrics_check->setGeometry(20,74,15,15);
feed_audience_metrics_label->setGeometry(40,72,375,19);
feed_channel_section_groupbox->setGeometry(10,97,sizeHint().width()/2-10,249);
feed_channel_section_groupbox->setGeometry(10,97,sizeHint().width()/2-10,289);
feed_channel_title_edit->setGeometry(115,112,375,19);
feed_channel_title_label->setGeometry(20,112,90,19);
feed_channel_category_edit->setGeometry(115,134,375,19);
@ -645,51 +707,58 @@ void EditFeed::resizeEvent(QResizeEvent *e)
feed_channel_language_label->setGeometry(20,244,90,19);
feed_channel_description_edit->setGeometry(115,266,375,76);
feed_channel_description_label->setGeometry(20,266,90,19);
feed_channel_image_box->setGeometry(115,344,375,38);
feed_channel_image_box->setIconSize(QSize(36,36));
feed_channel_image_label->setGeometry(20,344,90,19);
feed_purge_url_edit->setGeometry(155,354,335,19);
feed_purge_url_label->setGeometry(20,354,130,19);
feed_purge_username_edit->setGeometry(225,376,95,19);
feed_purge_username_label->setGeometry(40,376,180,19);
feed_purge_password_edit->setGeometry(395,376,95,19);
feed_purge_password_label->setGeometry(320,376,70,19);
feed_purge_url_edit->setGeometry(155,394,335,19);
feed_purge_url_label->setGeometry(20,394,130,19);
feed_purge_username_edit->setGeometry(225,416,95,19);
feed_purge_username_label->setGeometry(40,416,180,19);
feed_purge_password_edit->setGeometry(395,416,95,19);
feed_purge_password_label->setGeometry(320,416,70,19);
feed_format_edit->setGeometry(155,398,285,20);
feed_format_label->setGeometry(5,398,145,20);
feed_format_button->setGeometry(450,398,40,24);
feed_format_edit->setGeometry(155,438,285,20);
feed_format_label->setGeometry(5,438,145,20);
feed_format_button->setGeometry(450,438,40,24);
feed_normalize_check->setGeometry(155,422,15,15);
feed_normalize_check_label->setGeometry(175,420,83,20);
feed_normalize_spin->setGeometry(295,418,40,20);
feed_normalize_label->setGeometry(245,418,45,20);
feed_normalize_unit_label->setGeometry(340,418,40,20);
feed_normalize_check->setGeometry(155,462,15,15);
feed_normalize_check_label->setGeometry(175,460,83,20);
feed_normalize_spin->setGeometry(295,458,40,20);
feed_normalize_label->setGeometry(245,458,45,20);
feed_normalize_unit_label->setGeometry(340,458,40,20);
feed_base_url_edit->setGeometry(155,442,335,19);
feed_base_url_label->setGeometry(5,442,145,19);
feed_keep_metadata_box->setGeometry(155,464,15,15);
feed_keep_metadata_label->setGeometry(175,462,180,19);
feed_autopost_box->setGeometry(365,464,15,15);
feed_autopost_label->setGeometry(385,462,200,19);
feed_base_url_edit->setGeometry(155,482,335,19);
feed_base_url_label->setGeometry(5,482,145,19);
feed_keep_metadata_box->setGeometry(155,504,15,15);
feed_keep_metadata_label->setGeometry(175,502,180,19);
feed_autopost_box->setGeometry(365,504,15,15);
feed_autopost_label->setGeometry(385,502,200,19);
feed_base_preamble_edit->setGeometry(155,486,335,19);
feed_base_preamble_label->setGeometry(20,486,130,19);
feed_base_preamble_edit->setGeometry(155,526,335,19);
feed_base_preamble_label->setGeometry(20,526,130,19);
feed_extension_edit->setGeometry(155,508,70,19);
feed_extension_label->setGeometry(20,508,130,19);
feed_extension_edit->setGeometry(155,548,70,19);
feed_extension_label->setGeometry(20,548,130,19);
feed_max_shelf_life_spin->setGeometry(155,530,60,19);
feed_max_shelf_life_label->setGeometry(20,530,130,19);
feed_max_shelf_life_unit_label->setGeometry(220,530,50,19);
feed_max_shelf_life_spin->setGeometry(155,570,60,19);
feed_max_shelf_life_label->setGeometry(20,570,130,19);
feed_max_shelf_life_unit_label->setGeometry(220,570,50,19);
feed_castorder_box->setGeometry(155,552,100,19);
feed_castorder_label->setGeometry(20,552,130,19);
feed_castorder_box->setGeometry(155,592,100,19);
feed_castorder_label->setGeometry(20,592,130,19);
feed_media_link_mode_box->setGeometry(155,574,100,19);
feed_media_link_mode_label->setGeometry(20,574,130,19);
feed_media_link_mode_box->setGeometry(155,614,100,19);
feed_media_link_mode_label->setGeometry(20,614,130,19);
feed_redirect_check->setGeometry(20,606,15,15);
feed_redirect_label->setGeometry(40,606,200,19);
feed_redirect_url_edit->setGeometry(85,626,405,20);
feed_redirect_url_label->setGeometry(40,626,40,19);
feed_item_image_box->setGeometry(155,636,335,38);
feed_item_image_box->setIconSize(QSize(36,36));
feed_item_image_label->setGeometry(20,636,130,19);
feed_redirect_check->setGeometry(20,686,15,15);
feed_redirect_label->setGeometry(40,686,200,19);
feed_redirect_url_edit->setGeometry(85,706,405,20);
feed_redirect_url_label->setGeometry(40,706,40,19);
feed_rss_schema_label->setGeometry(520,10,90,19);
feed_rss_schema_box->setGeometry(615,10,200,19);

View File

@ -32,25 +32,29 @@
#include <rddialog.h>
#include <rdfeed.h>
#include <rdimagepickerbox.h>
#include <rdsettings.h>
#include <rdstation.h>
#include "list_images.h"
class EditFeed : public RDDialog
{
Q_OBJECT
public:
EditFeed(const QString &feed,QWidget *parent=0);
~EditFeed();
QSize sizeHint() const;
QSizePolicy sizePolicy() const;
private slots:
// void isSuperfeedChangedData(int n);
void comboboxActivatedData(int n);
void checkboxToggledData(bool state);
void lineeditChangedData(const QString &str);
void selectSubfeedsData();
void setFormatData();
void redirectToggledData(bool state);
void listImagesData();
void okData();
void cancelData();
@ -60,10 +64,12 @@ class EditFeed : public RDDialog
private:
void UpdateControlState();
RDFeed *feed_feed;
RDImagePickerModel *feed_image_model;
QLabel *feed_keyname_label;
QLineEdit *feed_keyname_edit;
QLabel *feed_is_superfeed_label;
QPushButton *feed_is_superfeed_button;
QPushButton *feed_list_images_button;
QCheckBox *feed_audience_metrics_check;
QLabel *feed_audience_metrics_label;
QComboBox *feed_is_superfeed_box;
@ -93,6 +99,7 @@ class EditFeed : public RDDialog
QCheckBox *feed_autopost_box;
QCheckBox *feed_keep_metadata_box;
RDSettings feed_settings;
ListImages *feed_images_dialog;
QLineEdit *feed_format_edit;
QCheckBox *feed_normalize_check;
QLabel *feed_normalize_label;
@ -112,6 +119,8 @@ class EditFeed : public RDDialog
QLabel *feed_channel_copyright_label;
QLabel *feed_channel_language_label;
QLabel *feed_channel_description_label;
QLabel *feed_channel_image_label;
RDImagePickerBox *feed_channel_image_box;
QLabel *feed_base_url_label;
QLabel *feed_base_preamble_label;
QLabel *feed_purge_url_label;
@ -125,6 +134,8 @@ class EditFeed : public RDDialog
QLabel *feed_castorder_label;
QLabel *feed_media_link_mode_label;
QLabel *feed_extension_label;
QLabel *feed_item_image_label;
RDImagePickerBox *feed_item_image_box;
QPushButton *feed_ok_button;
QPushButton *feed_cancel_button;
QLabel *feed_header_xml_label;

171
rdadmin/edit_image.cpp Normal file
View File

@ -0,0 +1,171 @@
// edit_image.cpp
//
// View a pixmap image and modify its metadata
//
// (C) Copyright 2020 Fred Gleason <fredg@paravelsystems.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include <rddb.h>
#include <rdescape_string.h>
#include <qapplication.h>
#include <qdesktopwidget.h>
#include "edit_image.h"
EditImage::EditImage(QWidget *parent)
: RDDialog(parent)
{
setWindowTitle("RDAdmin - "+tr("Image Viewer"));
setMinimumWidth(200);
setMinimumHeight(150);
c_image_label=new QLabel(this);
c_description_label=new QLabel(tr("Description")+":",this);
c_description_label->setAlignment(Qt::AlignRight);
c_description_label->setFont(labelFont());
c_description_edit=new QLineEdit(this);
c_ok_button=new QPushButton(tr("OK"),this);
c_ok_button->setFont(buttonFont());
connect(c_ok_button,SIGNAL(clicked()),this,SLOT(okData()));
c_cancel_button=new QPushButton(tr("Cancel"),this);
c_cancel_button->setFont(buttonFont());
connect(c_cancel_button,SIGNAL(clicked()),this,SLOT(cancelData()));
}
EditImage::~EditImage()
{
delete c_image_label;
delete c_description_label;
delete c_description_edit;
delete c_ok_button;
delete c_cancel_button;
}
QSize EditImage::sizeHint() const
{
return QSize(400,300);
}
int EditImage::exec(int img_id)
{
QString sql;
RDSqlQuery *q=NULL;
c_image_id=img_id;
sql=QString("select ")+
"DESCRIPTION,"+ // 00
"WIDTH,"+ // 01
"HEIGHT,"+ // 02
"DEPTH,"+ // 03
"DATA "+ // 04
"from FEED_IMAGES where "+
QString().sprintf("ID=%d",img_id);
q=new RDSqlQuery(sql);
if(q->first()) {
c_description_edit->setText(q->value(0).toString());
c_image=QImage();
c_image.loadFromData(q->value(4).toByteArray());
QSize fsize=FittedSize(c_image.size());
c_image_label->setPixmap(QPixmap::fromImage(c_image.
scaled(fsize,
Qt::KeepAspectRatio)));
resize(EDIT_IMAGE_WIDTH_OFFSET+fsize.width(),
EDIT_IMAGE_HEIGHT_OFFSET+fsize.height());
}
delete q;
return QDialog::exec();
}
void EditImage::okData()
{
QString sql=QString("update FEED_IMAGES set ")+
"DESCRIPTION=\""+RDEscapeString(c_description_edit->text())+"\" "+
QString().sprintf("where ID=%d",c_image_id);
RDSqlQuery::apply(sql);
done(true);
}
void EditImage::cancelData()
{
done(false);
}
void EditImage::closeEvent(QCloseEvent *e)
{
cancelData();
}
void EditImage::resizeEvent(QResizeEvent *e)
{
int w=size().width();
int h=size().height();
c_image_label->
setGeometry(10,2,w-EDIT_IMAGE_WIDTH_OFFSET,h-EDIT_IMAGE_HEIGHT_OFFSET);
c_image_label->setPixmap(QPixmap::fromImage(c_image.
scaled(c_image_label->size(),Qt::KeepAspectRatio)));
c_description_label->setGeometry(10,h-87,120,20);
c_description_edit->setGeometry(135,h-87,w-145,20);
c_ok_button->setGeometry(w-180,h-60,80,50);
c_cancel_button->setGeometry(w-90,h-60,80,50);
}
QSize EditImage::FittedSize(const QSize &img_size) const
{
QSize max_size=MaxFriendlyImageSize();
if((img_size.width()<=max_size.width())&&
(img_size.height()<=max_size.height())) {
return img_size;
}
QSize ret(img_size.boundedTo(max_size));
if(ret.height()==max_size.height()) {
ret.setWidth(img_size.width()*ret.height()/img_size.height());
}
else {
ret.setHeight(img_size.height()*ret.width()/img_size.width());
}
return ret;
}
QSize EditImage::MaxFriendlyImageSize() const
{
QDesktopWidget *dt=QApplication::desktop();
QSize dsize(dt->screenGeometry(dt->screenNumber(this)).size());
return QSize(dsize.width()-EDIT_IMAGE_WIDTH_OFFSET,
dsize.height()-EDIT_IMAGE_HEIGHT_OFFSET-100);
}

66
rdadmin/edit_image.h Normal file
View File

@ -0,0 +1,66 @@
// edit_image.h
//
// View a pixmap image and modify its metadata
//
// (C) Copyright 2020 Fred Gleason <fredg@paravelsystems.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#ifndef EDIT_IMAGE_H
#define EDIT_IMAGE_H
#include <qimage.h>
#include <qlabel.h>
#include <qlineedit.h>
#include <qpushbutton.h>
#include <rddialog.h>
#define EDIT_IMAGE_WIDTH_OFFSET 20
#define EDIT_IMAGE_HEIGHT_OFFSET 95
class EditImage : public RDDialog
{
Q_OBJECT
public:
EditImage(QWidget *parent=0);
~EditImage();
QSize sizeHint() const;
public slots:
int exec(int img_id);
private slots:
void okData();
void cancelData();
protected:
void closeEvent(QCloseEvent *e);
void resizeEvent(QResizeEvent *e);
private:
QSize FittedSize(const QSize &img_size) const;
QSize MaxFriendlyImageSize() const;
QLabel *c_image_label;
QLabel *c_description_label;
QLineEdit *c_description_edit;
QPushButton *c_ok_button;
QPushButton *c_cancel_button;
QImage c_image;
int c_image_id;
};
#endif // EDIT_IMAGE_H

330
rdadmin/list_images.cpp Normal file
View File

@ -0,0 +1,330 @@
// list_images.cpp
//
// Manage a collection of pixmap images
//
// (C) Copyright 2020 Fred Gleason <fredg@paravelsystems.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#include <qfiledialog.h>
#include <qmessagebox.h>
#include <qprogressdialog.h>
#include <rdescape_string.h>
#include <rdupload.h>
#include "list_images.h"
ListImages::ListImages(RDImagePickerModel *model,QWidget *parent)
: RDDialog(parent)
{
list_model=model;
setWindowTitle("RDAdmin - "+tr("Image Manager"));
//
// Dialogs
//
list_edit_image_dialog=new EditImage(this);
if(getenv("HOME")!=NULL) {
list_file_dir=getenv("HOME");
}
else {
list_file_dir="/";
}
list_view=new QListView(this);
connect(list_view,SIGNAL(clicked(const QModelIndex &)),
this,SLOT(clickedData(const QModelIndex &)));
connect(list_view,SIGNAL(doubleClicked(const QModelIndex &)),
this,SLOT(doubleClickedData(const QModelIndex &)));
list_view->setModel(list_model);
list_add_button=new QPushButton(tr("Add"),this);
list_add_button->setFont(buttonFont());
connect(list_add_button,SIGNAL(clicked()),this,SLOT(addData()));
list_view_button=new QPushButton(tr("View"),this);
list_view_button->setFont(buttonFont());
// list_view_button->setDisabled(true);
connect(list_view_button,SIGNAL(clicked()),this,SLOT(viewData()));
list_delete_button=new QPushButton(tr("Delete"),this);
list_delete_button->setFont(buttonFont());
// list_delete_button->setDisabled(true);
connect(list_delete_button,SIGNAL(clicked()),this,SLOT(deleteData()));
list_close_button=new QPushButton(tr("Close"),this);
list_close_button->setFont(buttonFont());
connect(list_close_button,SIGNAL(clicked()),this,SLOT(closeData()));
}
ListImages::~ListImages()
{
delete list_close_button;
delete list_delete_button;
delete list_view_button;
delete list_add_button;
delete list_view;
delete list_edit_image_dialog;
}
QSize ListImages::sizeHint() const
{
return QSize(400,300);
}
int ListImages::exec(RDFeed *feed)
{
list_feed=feed;
list_model->setCategoryId(feed->id());
return QDialog::exec();
}
void ListImages::addData()
{
// RDUpload::ErrorCode err_code;
QStringList f0;
int img_id=-1;
QString err_msg="";
QString filename=
QFileDialog::getOpenFileName(this,
"RDAdmin - "+tr("Open Podcast Image File"),
list_file_dir,RD_PODCAST_IMAGE_FILE_FILTER);
if(!filename.isNull()) {
//
// Import the image
//
if((img_id=list_feed->importImageFile(filename,&err_msg))<0) {
QMessageBox::warning(this,"RDAdmin - "+tr("Import Error"),
tr("Image import failed.")+"\n"+
"["+err_msg+"].");
return;
}
//
// Upload the image
//
f0=filename.split(".",QString::SkipEmptyParts);
if(!UploadRemoteImage(filename,list_feed->purgeUrl()+"/"+
RDFeed::imageFilename(list_feed->id(),
img_id,f0.last()),
list_feed->purgeUsername(),
list_feed->purgePassword(),
&err_msg)){
QMessageBox::warning(this,"RDAdmin - "+tr("Upload Error"),
tr("Image upload failed!")+"\n"+
"["+err_msg+"].");
list_feed->deleteImage(img_id,&err_msg);
}
/*
RDUpload *upload=new RDUpload(rda->config(),this);
upload->setSourceFile(filename);
upload->setDestinationUrl(list_feed->purgeUrl()+"/"+
RDFeed::imageFilename(list_feed->id(),img_id,
f0.last()));
QProgressDialog *pd=new QProgressDialog(tr("Uploading image..."),"",
0,upload->totalSteps(),this);
connect(upload,SIGNAL(progressChanged(int)),pd,SLOT(setValue(int)));
if((err_code=upload->runUpload(list_feed->purgeUsername(),
list_feed->purgePassword(),false))!=
RDUpload::ErrorOk) {
QMessageBox::warning(this,"RDAdmin - "+tr("Upload Error"),
tr("Image upload failed!")+"\n"+
"["+RDUpload::errorText(err_code)+"].");
list_feed->deleteImage(img_id,&err_msg);
delete upload;
return;
}
delete pd;
delete upload;
*/
//
// Open dialog for setting the metadata
//
if(!list_edit_image_dialog->exec(img_id)) {
list_feed->deleteImage(img_id,&err_msg);
return;
}
list_model->refresh();
//
// Save import path
//
f0=filename.split("/",QString::SkipEmptyParts);
f0.removeLast();
list_file_dir=f0.join("/");
}
}
void ListImages::viewData()
{
int row;
if((row=SelectedRow())>=0) {
if(list_edit_image_dialog->exec(list_model->imageId(row))) {
list_model->update(row);
}
}
}
void ListImages::deleteData()
{
int row;
QString err_msg="";
QString sql;
RDSqlQuery *q=NULL;
if((row=SelectedRow())>=0) {
sql=QString("select FILE_EXTENSION from FEED_IMAGES where ")+
QString().sprintf("ID=%d",list_model->imageId(row));
q=new RDSqlQuery(sql);
if(q->first()) {
if((row=SelectedRow())>=0) {
if(QMessageBox::question(this,"RDAdmin - "+tr("Delete Podcast Image"),
tr("Are you sure you want to delete this image?"),
QMessageBox::Yes,QMessageBox::No)!=
QMessageBox::Yes) {
return;
}
if(!DeleteRemoteImage(list_feed->purgeUrl()+"/"+
RDFeed::imageFilename(list_feed->id(),
list_model->imageId(row),
q->value(0).toString()),
list_feed->purgeUsername(),
list_feed->purgePassword(),&err_msg)) {
if(QMessageBox::information(this,"RDAdmin - "+tr("Delete Error"),
tr("Unable to delete remote file!")+"\n"+
"["+err_msg+"].\n"+
"\n"+
tr("Delete local data?"),
QMessageBox::Yes,QMessageBox::No)!=
QMessageBox::Yes) {
return;
}
}
if(list_feed->deleteImage(list_model->imageId(row),&err_msg)) {
list_model->refresh();
}
else {
QMessageBox::warning(this,"RDAdmin - "+tr("Error"),
tr("Image deletion failed!")+"\n"+
"["+err_msg+"].");
}
}
}
}
}
void ListImages::clickedData(const QModelIndex &index)
{
list_view_button->
setDisabled(list_view->model()->data(index,Qt::DecorationRole).isNull());
list_delete_button->
setDisabled(list_view->model()->data(index,Qt::DecorationRole).isNull());
}
void ListImages::doubleClickedData(const QModelIndex &index)
{
viewData();
}
void ListImages::closeData()
{
done(true);
}
void ListImages::closeEvent(QCloseEvent *e)
{
closeData();
}
void ListImages::resizeEvent(QResizeEvent *e)
{
int w=size().width();
int h=size().height();
list_model->rescaleImages(QSize(40,40));
list_view->setGeometry(10,2,w-20,h-70);
list_add_button->setGeometry(15,h-60,60,37);
list_view_button->setGeometry(95,h-60,60,37);
list_delete_button->setGeometry(175,h-60,60,37);
list_close_button->setGeometry(w-90,h-60,80,50);
}
int ListImages::SelectedRow() const
{
QModelIndexList indexes=list_view->selectionModel()->selectedIndexes();
if(indexes.size()>0) {
return indexes.at(0).row();
}
return -1;
}
bool ListImages::UploadRemoteImage(const QString &filename,const QString &url,
const QString &username,
const QString &password,QString *err_msg)
{
RDUpload::ErrorCode err_code;
RDUpload *upload=new RDUpload(rda->config(),this);
upload->setSourceFile(filename);
upload->setDestinationUrl(url);
QProgressDialog *pd=new QProgressDialog(tr("Uploading image..."),"",
0,upload->totalSteps(),this);
pd->setWindowTitle("RDAdmin");
connect(upload,SIGNAL(progressChanged(int)),pd,SLOT(setValue(int)));
err_code=upload->runUpload(username,password,false);
delete pd;
delete upload;
return err_code==RDUpload::ErrorOk;
}
bool ListImages::DeleteRemoteImage(const QString &url,const QString &username,
const QString &password,QString *err_msg)
{
RDDelete::ErrorCode err_code;
RDDelete *del=new RDDelete(rda->config(),this);
del->setTargetUrl(url);
err_code=del->runDelete(username,password,false);
*err_msg=RDDelete::errorText(err_code);
delete del;
return err_code==RDDelete::ErrorOk;
}

76
rdadmin/list_images.h Normal file
View File

@ -0,0 +1,76 @@
// list_images.h
//
// Manage a collection of pixmap images
//
// (C) Copyright 2020 Fred Gleason <fredg@paravelsystems.com>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2 as
// published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public
// License along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//
#ifndef LIST_IMAGES_H
#define LIST_IMAGES_H
#include <qlistview.h>
#include <qpushbutton.h>
#include <rddelete.h>
#include <rddialog.h>
#include <rdfeed.h>
#include <rdimagepickermodel.h>
#include "edit_image.h"
class ListImages : public RDDialog
{
Q_OBJECT
public:
ListImages(RDImagePickerModel *model,QWidget *parent=0);
~ListImages();
QSize sizeHint() const;
public slots:
int exec(RDFeed *feed);
private slots:
void addData();
void viewData();
void deleteData();
void clickedData(const QModelIndex &index);
void doubleClickedData(const QModelIndex &index);
void closeData();
protected:
void closeEvent(QCloseEvent *e);
void resizeEvent(QResizeEvent *e);
private:
int SelectedRow() const;
bool UploadRemoteImage(const QString &filename,const QString &url,
const QString &username,const QString &password,
QString *err_msg);
bool DeleteRemoteImage(const QString &url,const QString &username,
const QString &password,QString *err_msg);
EditImage *list_edit_image_dialog;
QListView *list_view;
RDImagePickerModel *list_model;
RDFeed *list_feed;
QPushButton *list_add_button;
QPushButton *list_view_button;
QPushButton *list_delete_button;
QPushButton *list_close_button;
QString list_file_dir;
};
#endif // LIST_IMAGES_H

View File

@ -41,6 +41,7 @@ x11 {
SOURCES += edit_group.cpp
SOURCES += edit_hostvar.cpp
SOURCES += edit_hotkeys.cpp
SOURCES += edit_image.cpp
SOURCES += edit_jack.cpp
SOURCES += edit_jack_client.cpp
SOURCES += edit_livewiregpio.cpp
@ -68,6 +69,7 @@ x11 {
SOURCES += list_gpis.cpp
SOURCES += list_groups.cpp
SOURCES += list_hostvars.cpp
SOURCES += list_images.cpp
SOURCES += list_livewiregpios.cpp
SOURCES += list_matrices.cpp
SOURCES += list_pypads.cpp
@ -109,6 +111,7 @@ x11 {
HEADERS += edit_group.h
HEADERS += edit_hostvar.h
HEADERS += edit_hotkeys.h
HEADERS += edit_image.h
HEADERS += edit_jack.h
HEADERS += edit_jack_client.h
HEADERS += edit_livewiregpio.h
@ -136,6 +139,7 @@ x11 {
HEADERS += list_gpis.h
HEADERS += list_groups.h
HEADERS += list_hostvars.h
HEADERS += list_images.h
HEADERS += list_livewiregpios.h
HEADERS += list_matrices.h
HEADERS += list_pypads.h

View File

@ -1673,6 +1673,22 @@ Feeds</source>
<source>RSS Schema</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default Item Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Manage</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Images</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditFeedPerms</name>
@ -1947,6 +1963,25 @@ Stále ještě chcete uložit?</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditImage</name>
<message>
<source>Description</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>OK</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished">Zrušit</translation>
</message>
<message>
<source>Image Viewer</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditJack</name>
<message>
@ -5304,6 +5339,81 @@ Stále ještě jej chcete smazat?</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ListImages</name>
<message>
<source>Add</source>
<translation type="unfinished">Přidat</translation>
</message>
<message>
<source>View</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete</source>
<translation type="unfinished">Smazat</translation>
</message>
<message>
<source>Close</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image Manager</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Open Podcast Image File</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Import Error</source>
<translation type="unfinished">Chyba při zavedení</translation>
</message>
<message>
<source>Image import failed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete Podcast Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to delete this image?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image deletion failed!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Upload Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image upload failed!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unable to delete remote file!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete local data?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Uploading image...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ListLiveWireGpios</name>
<message>

View File

@ -1556,6 +1556,22 @@ Feeds</source>
<source>RSS Schema</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default Item Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Manage</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Images</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditFeedPerms</name>
@ -1829,6 +1845,25 @@ Do you still want to save?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditImage</name>
<message>
<source>Description</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>OK</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image Viewer</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditJack</name>
<message>
@ -5134,6 +5169,81 @@ Generieren</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ListImages</name>
<message>
<source>Add</source>
<translation type="unfinished">Hinzufügen</translation>
</message>
<message>
<source>View</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete</source>
<translation type="unfinished">Löschen</translation>
</message>
<message>
<source>Close</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image Manager</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Open Podcast Image File</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Import Error</source>
<translation type="unfinished">Importfehler</translation>
</message>
<message>
<source>Image import failed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete Podcast Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to delete this image?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image deletion failed!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Upload Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image upload failed!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unable to delete remote file!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete local data?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Uploading image...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ListLiveWireGpios</name>
<message>

View File

@ -1676,6 +1676,22 @@ Feeds</source>
<source>RSS Schema</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default Item Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Manage</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Images</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditFeedPerms</name>
@ -1949,6 +1965,25 @@ Do you still want to save?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditImage</name>
<message>
<source>Description</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>OK</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished">Cancelar</translation>
</message>
<message>
<source>Image Viewer</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditJack</name>
<message>
@ -5271,6 +5306,81 @@ Do you still want to delete it?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ListImages</name>
<message>
<source>Add</source>
<translation type="unfinished">Añadir</translation>
</message>
<message>
<source>View</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete</source>
<translation type="unfinished">Borrar</translation>
</message>
<message>
<source>Close</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image Manager</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Open Podcast Image File</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Import Error</source>
<translation type="unfinished">Error de importación</translation>
</message>
<message>
<source>Image import failed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete Podcast Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to delete this image?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image deletion failed!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Upload Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image upload failed!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unable to delete remote file!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete local data?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Uploading image...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ListLiveWireGpios</name>
<message>

View File

@ -1228,6 +1228,22 @@ Feeds</source>
<source>RSS Schema</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default Item Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Manage</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Images</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditFeedPerms</name>
@ -1499,6 +1515,25 @@ Do you still want to save?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditImage</name>
<message>
<source>Description</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>OK</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image Viewer</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditJack</name>
<message>
@ -4283,6 +4318,81 @@ Permissions</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ListImages</name>
<message>
<source>Add</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>View</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Close</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image Manager</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Open Podcast Image File</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Import Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image import failed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete Podcast Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to delete this image?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image deletion failed!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Upload Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image upload failed!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unable to delete remote file!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete local data?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Uploading image...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ListLiveWireGpios</name>
<message>

View File

@ -1518,6 +1518,22 @@ Feeds</source>
<source>RSS Schema</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default Item Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Manage</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Images</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditFeedPerms</name>
@ -1796,6 +1812,25 @@ Vil du framleis lagra?</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditImage</name>
<message>
<source>Description</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>OK</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image Viewer</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditJack</name>
<message>
@ -4999,6 +5034,81 @@ Klikk på &quot;Lisens&quot;-knappen for fleire opplysningar.</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ListImages</name>
<message>
<source>Add</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>View</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Close</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image Manager</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Open Podcast Image File</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Import Error</source>
<translation type="unfinished">Importfeil</translation>
</message>
<message>
<source>Image import failed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete Podcast Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to delete this image?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image deletion failed!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Upload Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image upload failed!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unable to delete remote file!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete local data?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Uploading image...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ListLiveWireGpios</name>
<message>

View File

@ -1518,6 +1518,22 @@ Feeds</source>
<source>RSS Schema</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default Item Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Manage</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Images</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditFeedPerms</name>
@ -1796,6 +1812,25 @@ Vil du framleis lagra?</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditImage</name>
<message>
<source>Description</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>OK</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image Viewer</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditJack</name>
<message>
@ -4999,6 +5034,81 @@ Klikk på &quot;Lisens&quot;-knappen for fleire opplysningar.</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ListImages</name>
<message>
<source>Add</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>View</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Close</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image Manager</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Open Podcast Image File</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Import Error</source>
<translation type="unfinished">Importfeil</translation>
</message>
<message>
<source>Image import failed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete Podcast Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to delete this image?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image deletion failed!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Upload Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image upload failed!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unable to delete remote file!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete local data?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Uploading image...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ListLiveWireGpios</name>
<message>

View File

@ -1528,6 +1528,22 @@ Feeds</source>
<source>RSS Schema</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Default Item Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Manage</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Images</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditFeedPerms</name>
@ -1802,6 +1818,25 @@ Você ainda quer salvar?</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditImage</name>
<message>
<source>Description</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>OK</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Cancel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image Viewer</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditJack</name>
<message>
@ -5114,6 +5149,81 @@ Você ainda quer Deletar?</translation>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ListImages</name>
<message>
<source>Add</source>
<translation type="unfinished">Adicionar</translation>
</message>
<message>
<source>View</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete</source>
<translation type="unfinished">Deletar</translation>
</message>
<message>
<source>Close</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image Manager</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Open Podcast Image File</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Import Error</source>
<translation type="unfinished">Erro ao Importar</translation>
</message>
<message>
<source>Image import failed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete Podcast Image</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Are you sure you want to delete this image?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image deletion failed!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Upload Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image upload failed!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete Error</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Unable to delete remote file!</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete local data?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Uploading image...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ListLiveWireGpios</name>
<message>

View File

@ -41,6 +41,17 @@ bool MainObject::RevertSchema(int cur_schema,int set_schema,QString *err_msg)
//
// Revert 324
//
if((cur_schema==324)&&(set_schema<cur_schema)) {
DropColumn("PODCASTS","ITEM_IMAGE_ID");
DropColumn("FEEDS","DEFAULT_ITEM_IMAGE_ID");
DropColumn("FEEDS","CHANNEL_IMAGE_ID");
WriteSchemaVersion(--cur_schema);
}
//
// Revert 323
//

View File

@ -160,7 +160,7 @@ void MainObject::InitializeSchemaMap() {
global_version_map["3.1"]=310;
global_version_map["3.2"]=311;
global_version_map["3.3"]=314;
global_version_map["3.4"]=323;
global_version_map["3.4"]=324;
}

View File

@ -10043,13 +10043,14 @@ bool MainObject::UpdateSchema(int cur_schema,int set_schema,QString *err_msg)
if((cur_schema<323)&&(set_schema>cur_schema)) {
sql=QString("create table FEED_IMAGES (")+
"ID int unsigned primary key,"+
"ID int unsigned primary key auto_increment,"+
"FEED_ID int unsigned not null,"+
"FEED_KEY_NAME varchar(8) not null,"+
"WIDTH int not null,"+\
"HEIGHT int not null,"+
"DEPTH int not null,"+
"DESCRIPTION text,"+
"DESCRIPTION varchar(191) not null,"+
"FILE_EXTENSION varchar(10) not null,"+
"DATA mediumblob not null,"+
"index FEED_ID_IDX (FEED_ID),"+
"index FEED_KEY_NAME_IDX (FEED_KEY_NAME))";
@ -10060,6 +10061,26 @@ bool MainObject::UpdateSchema(int cur_schema,int set_schema,QString *err_msg)
WriteSchemaVersion(++cur_schema);
}
if((cur_schema<324)&&(set_schema>cur_schema)) {
sql=QString("alter table FEEDS add column CHANNEL_IMAGE_ID ")+
"int not null default -1 after CHANNEL_LANGUAGE";
if(!RDSqlQuery::apply(sql,err_msg)) {
return false;
}
sql=QString("alter table FEEDS add column DEFAULT_ITEM_IMAGE_ID ")+
"int not null default -1 after KEEP_METADATA";
if(!RDSqlQuery::apply(sql,err_msg)) {
return false;
}
sql=QString("alter table PODCASTS add column ITEM_IMAGE_ID ")+
"int not null default -1 after ITEM_SOURCE_URL";
if(!RDSqlQuery::apply(sql,err_msg)) {
return false;
}
WriteSchemaVersion(++cur_schema);
}
// NEW SCHEMA UPDATES GO HERE...