Merge pull request #1323 from csoler/v0.6-ForumAdmin

V0.6 forum admin
This commit is contained in:
csoler 2018-09-11 15:17:33 +02:00 committed by GitHub
commit b138263878
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 666 additions and 367 deletions

View file

@ -111,7 +111,7 @@ public:
RsTlvKeySignatureSet signSet; RsTlvKeySignatureSet signSet;
std::string mMsgName; std::string mMsgName;
time_t mPublishTs; time_t mPublishTs;
uint32_t mMsgFlags; // Whats this for? uint32_t mMsgFlags; // used by some services (e.g. by forums to store message moderation flags)
// BELOW HERE IS LOCAL DATA, THAT IS NOT FROM MSG. // BELOW HERE IS LOCAL DATA, THAT IS NOT FROM MSG.
// normally READ / UNREAD flags. LOCAL Data. // normally READ / UNREAD flags. LOCAL Data.

View file

@ -115,8 +115,7 @@ namespace GXS_SERV {
/** START GXS Grp status flags **/ /** START GXS Grp status flags **/
static const uint32_t GXS_GRP_STATUS_UNPROCESSED = 0x000000100; static const uint32_t GXS_GRP_STATUS_UNPROCESSED = 0x000000100;
static const uint32_t GXS_GRP_STATUS_UNREAD = 0x000000200;
static const uint32_t GXS_GRP_STATUS_UNREAD = 0x00000200;
/** END GXS Grp status flags **/ /** END GXS Grp status flags **/
} }

View file

@ -28,8 +28,15 @@
#include "retroshare/rstokenservice.h" #include "retroshare/rstokenservice.h"
#include "retroshare/rsgxsifacehelper.h" #include "retroshare/rsgxsifacehelper.h"
#include "serialiser/rstlvidset.h"
// Forum Service message flags, to be used in RsMsgMetaData::mMsgFlags
// Gxs imposes to use the first two bytes (lower bytes) of mMsgFlags for private forum flags, the upper bytes being used for internal GXS stuff.
static const uint32_t RS_GXS_FORUM_MSG_FLAGS_MASK = 0x0000000f ;
static const uint32_t RS_GXS_FORUM_MSG_FLAGS_MODERATED = 0x00000001 ;
#define IS_FORUM_MSG_MODERATION(flags) (flags & RS_GXS_FORUM_MSG_FLAGS_MODERATED)
/* The Main Interface Class - for information about your Peers */ /* The Main Interface Class - for information about your Peers */
class RsGxsForums; class RsGxsForums;
@ -40,6 +47,11 @@ class RsGxsForumGroup
public: public:
RsGroupMetaData mMeta; RsGroupMetaData mMeta;
std::string mDescription; std::string mDescription;
// What's below is optional, and handled by the serialiser
RsTlvGxsIdSet mAdminList;
RsTlvGxsMsgIdSet mPinnedPosts;
}; };
class RsGxsForumMsg class RsGxsForumMsg

View file

@ -138,7 +138,7 @@ struct RsMsgMetaData : RsSerializable
std::string mMsgName; std::string mMsgName;
time_t mPublishTs; time_t mPublishTs;
/// the first 16 bits for service, last 16 for GXS /// the lower 16 bits for service, upper 16 bits for GXS
uint32_t mMsgFlags; uint32_t mMsgFlags;
// BELOW HERE IS LOCAL DATA, THAT IS NOT FROM MSG. // BELOW HERE IS LOCAL DATA, THAT IS NOT FROM MSG.

View file

@ -48,7 +48,16 @@ void RsGxsForumGroupItem::clear()
void RsGxsForumGroupItem::serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx) void RsGxsForumGroupItem::serial_process(RsGenericSerializer::SerializeJob j,RsGenericSerializer::SerializeContext& ctx)
{ {
RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_DESCR,mGroup.mDescription,"mGroup.Description"); RsTypeSerializer::serial_process(j,ctx,TLV_TYPE_STR_DESCR,mGroup.mDescription,"mGroup.Description");
// This is for backward compatibility: normally all members are serialized, but in the previous version, these members are missing.
if(j == RsGenericSerializer::DESERIALIZE && ctx.mOffset == ctx.mSize)
return ;
RsTypeSerializer::serial_process<RsTlvItem>(j,ctx,mGroup.mAdminList ,"admin_list" ) ;
RsTypeSerializer::serial_process<RsTlvItem>(j,ctx,mGroup.mPinnedPosts,"pinned_posts") ;
} }
void RsGxsForumMsgItem::clear() void RsGxsForumMsgItem::clear()
{ {
mMsg.mMsg.clear(); mMsg.mMsg.clear();

View file

@ -200,6 +200,7 @@ const uint16_t TLV_TYPE_RECOGNSET = 0x1024;
const uint16_t TLV_TYPE_GXSIDSET = 0x1025; const uint16_t TLV_TYPE_GXSIDSET = 0x1025;
const uint16_t TLV_TYPE_GXSCIRCLEIDSET= 0x1026; const uint16_t TLV_TYPE_GXSCIRCLEIDSET= 0x1026;
const uint16_t TLV_TYPE_NODEGROUPIDSET= 0x1027; const uint16_t TLV_TYPE_NODEGROUPIDSET= 0x1027;
const uint16_t TLV_TYPE_GXSMSGIDSET = 0x1028;
const uint16_t TLV_TYPE_SERVICESET = 0x1030; const uint16_t TLV_TYPE_SERVICESET = 0x1030;

View file

@ -114,12 +114,13 @@ template<class ID_CLASS,uint32_t TLV_TYPE> class t_RsTlvIdSet: public RsTlvItem
std::set<ID_CLASS> ids ; std::set<ID_CLASS> ids ;
}; };
typedef t_RsTlvIdSet<RsPeerId, TLV_TYPE_PEERSET> RsTlvPeerIdSet ; typedef t_RsTlvIdSet<RsPeerId, TLV_TYPE_PEERSET> RsTlvPeerIdSet ;
typedef t_RsTlvIdSet<RsPgpId, TLV_TYPE_PGPIDSET> RsTlvPgpIdSet ; typedef t_RsTlvIdSet<RsPgpId, TLV_TYPE_PGPIDSET> RsTlvPgpIdSet ;
typedef t_RsTlvIdSet<Sha1CheckSum, TLV_TYPE_HASHSET> RsTlvHashSet ; typedef t_RsTlvIdSet<Sha1CheckSum, TLV_TYPE_HASHSET> RsTlvHashSet ;
typedef t_RsTlvIdSet<RsGxsId, TLV_TYPE_GXSIDSET> RsTlvGxsIdSet ; typedef t_RsTlvIdSet<RsGxsId, TLV_TYPE_GXSIDSET> RsTlvGxsIdSet ;
typedef t_RsTlvIdSet<RsGxsCircleId,TLV_TYPE_GXSCIRCLEIDSET> RsTlvGxsCircleIdSet ; typedef t_RsTlvIdSet<RsGxsMessageId,TLV_TYPE_GXSMSGIDSET> RsTlvGxsMsgIdSet ;
typedef t_RsTlvIdSet<RsNodeGroupId,TLV_TYPE_NODEGROUPIDSET> RsTlvNodeGroupIdSet ; typedef t_RsTlvIdSet<RsGxsCircleId, TLV_TYPE_GXSCIRCLEIDSET> RsTlvGxsCircleIdSet ;
typedef t_RsTlvIdSet<RsNodeGroupId, TLV_TYPE_NODEGROUPIDSET> RsTlvNodeGroupIdSet ;
class RsTlvServiceIdSet: public RsTlvItem class RsTlvServiceIdSet: public RsTlvItem
{ {

View file

@ -26,8 +26,8 @@
RsPhoto *rsPhoto = NULL; RsPhoto *rsPhoto = NULL;
const uint32_t RsPhoto::FLAG_MSG_TYPE_MASK = 0x000f; const uint32_t RsPhoto::FLAG_MSG_TYPE_MASK = 0x000f;
const uint32_t RsPhoto::FLAG_MSG_TYPE_PHOTO_POST = 0x0001; const uint32_t RsPhoto::FLAG_MSG_TYPE_PHOTO_POST = 0x0001;
const uint32_t RsPhoto::FLAG_MSG_TYPE_PHOTO_COMMENT = 0x0002; const uint32_t RsPhoto::FLAG_MSG_TYPE_PHOTO_COMMENT = 0x0002;

View file

@ -1539,7 +1539,7 @@ void SharedFilesDialog::FilterItems()
if(text.length() < 3) if(text.length() < 3)
return ; return ;
FileSearchFlags flags = isRemote()?RS_FILE_HINTS_REMOTE:RS_FILE_HINTS_LOCAL; //FileSearchFlags flags = isRemote()?RS_FILE_HINTS_REMOTE:RS_FILE_HINTS_LOCAL;
QStringList lst = text.split(" ",QString::SkipEmptyParts) ; QStringList lst = text.split(" ",QString::SkipEmptyParts) ;
std::list<std::string> keywords ; std::list<std::string> keywords ;

View file

@ -32,7 +32,7 @@ const uint32_t PostedCreateEnabledFlags = (
GXS_GROUP_FLAGS_DESCRIPTION | GXS_GROUP_FLAGS_DESCRIPTION |
GXS_GROUP_FLAGS_DISTRIBUTION | GXS_GROUP_FLAGS_DISTRIBUTION |
// GXS_GROUP_FLAGS_PUBLISHSIGN | // GXS_GROUP_FLAGS_PUBLISHSIGN |
GXS_GROUP_FLAGS_SHAREKEYS | // GXS_GROUP_FLAGS_SHAREKEYS | // disabled because the UI doesn't handle it yet.
// GXS_GROUP_FLAGS_PERSONALSIGN | // GXS_GROUP_FLAGS_PERSONALSIGN |
// GXS_GROUP_FLAGS_COMMENTS | // GXS_GROUP_FLAGS_COMMENTS |
0); 0);
@ -84,7 +84,7 @@ void PostedGroupDialog::initUi()
break; break;
} }
setUiText(UITYPE_KEY_SHARE_CHECKBOX, tr("Add Topic Admins")); setUiText(UITYPE_ADD_ADMINS_CHECKBOX, tr("Add Topic Admins"));
setUiText(UITYPE_CONTACTS_DOCK, tr("Select Topic Admins")); setUiText(UITYPE_CONTACTS_DOCK, tr("Select Topic Admins"));
} }

View file

@ -288,9 +288,15 @@ void FriendSelectionWidget::secured_fillList()
} }
std::set<RsGxsId> gxsIdsSelected; std::set<RsGxsId> gxsIdsSelected;
if (mShowTypes & SHOW_GXS) if (mShowTypes & SHOW_GXS)
{
selectedIds<RsGxsId,IDTYPE_GXS>(gxsIdsSelected,true); selectedIds<RsGxsId,IDTYPE_GXS>(gxsIdsSelected,true);
if(!ui->friendList->topLevelItemCount()) // if not loaded yet, use the existing list.
gxsIdsSelected = mPreSelectedGxsIds;
}
std::set<RsGxsId> gxsIdsSelected2; std::set<RsGxsId> gxsIdsSelected2;
if (mShowTypes & SHOW_CONTACTS) if (mShowTypes & SHOW_CONTACTS)
selectedIds<RsGxsId,IDTYPE_GXS>(gxsIdsSelected2,true); selectedIds<RsGxsId,IDTYPE_GXS>(gxsIdsSelected2,true);
@ -683,6 +689,11 @@ void FriendSelectionWidget::requestGXSIdList()
mIdQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, IDDIALOG_IDLIST); mIdQueue->requestGroupInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, IDDIALOG_IDLIST);
} }
template<> void FriendSelectionWidget::setSelectedIds<RsGxsId,FriendSelectionWidget::IDTYPE_GXS>(const std::set<RsGxsId>& ids, bool add)
{
mPreSelectedGxsIds = ids ;
requestGXSIdList();
}
void FriendSelectionWidget::groupsChanged(int /*type*/) void FriendSelectionWidget::groupsChanged(int /*type*/)
{ {

View file

@ -174,6 +174,8 @@ private:
std::vector<RsGxsGroupId> gxsIds ; std::vector<RsGxsGroupId> gxsIds ;
TokenQueue *mIdQueue ; TokenQueue *mIdQueue ;
QList<QAction*> mContextMenuActions; QList<QAction*> mContextMenuActions;
std::set<RsGxsId> mPreSelectedGxsIds; // because loading of GxsIds is asynchroneous we keep selected Ids from the client in a list here and use it to initialize after loading them.
}; };
Q_DECLARE_OPERATORS_FOR_FLAGS(FriendSelectionWidget::ShowTypes) Q_DECLARE_OPERATORS_FOR_FLAGS(FriendSelectionWidget::ShowTypes)

View file

@ -101,6 +101,7 @@ void GxsGroupDialog::init()
connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(submitGroup())); connect(ui.buttonBox, SIGNAL(accepted()), this, SLOT(submitGroup()));
connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(cancelDialog())); connect(ui.buttonBox, SIGNAL(rejected()), this, SLOT(cancelDialog()));
connect(ui.pubKeyShare_cb, SIGNAL(clicked()), this, SLOT(setShareList())); connect(ui.pubKeyShare_cb, SIGNAL(clicked()), this, SLOT(setShareList()));
connect(ui.addAdmins_cb, SIGNAL(clicked()), this, SLOT(setAdminsList()));
connect(ui.groupLogo, SIGNAL(clicked() ), this , SLOT(addGroupLogo())); connect(ui.groupLogo, SIGNAL(clicked() ), this , SLOT(addGroupLogo()));
connect(ui.addLogoButton, SIGNAL(clicked() ), this , SLOT(addGroupLogo())); connect(ui.addLogoButton, SIGNAL(clicked() ), this , SLOT(addGroupLogo()));
@ -115,14 +116,25 @@ void GxsGroupDialog::init()
if (!ui.pubKeyShare_cb->isChecked()) if (!ui.pubKeyShare_cb->isChecked())
{ {
ui.contactsdockWidget->hide(); ui.shareKeyList->hide();
this->resize(this->size().width() - ui.contactsdockWidget->size().width(), this->size().height()); //this->resize(this->size().width() - ui.contactsdockWidget->size().width(), this->size().height());
}
if (!ui.addAdmins_cb->isChecked())
{
ui.adminsList->hide();
//this->resize(this->size().width() - ui.contactsdockWidget->size().width(), this->size().height());
} }
/* initialize key share list */ /* initialize key share list */
ui.keyShareList->setHeaderText(tr("Contacts:")); ui.shareKeyList->setHeaderText(tr("Contacts:"));
ui.keyShareList->setModus(FriendSelectionWidget::MODUS_CHECK); ui.shareKeyList->setModus(FriendSelectionWidget::MODUS_CHECK);
ui.keyShareList->start(); ui.shareKeyList->start();
/* initialize key share list */
ui.adminsList->setHeaderText(tr("Moderators:"));
ui.adminsList->setModus(FriendSelectionWidget::MODUS_CHECK);
ui.adminsList->setShowType(FriendSelectionWidget::SHOW_GXS);
ui.adminsList->start();
/* Setup Reasonable Defaults */ /* Setup Reasonable Defaults */
@ -165,7 +177,8 @@ void GxsGroupDialog::setUiText(UiType uiType, const QString &text)
ui.pubKeyShare_cb->setText(text); ui.pubKeyShare_cb->setText(text);
break; break;
case UITYPE_CONTACTS_DOCK: case UITYPE_CONTACTS_DOCK:
ui.contactsdockWidget->setWindowTitle(text); case UITYPE_ADD_ADMINS_CHECKBOX:
//ui.contactsdockWidget->setWindowTitle(text);
break; break;
case UITYPE_BUTTONBOX_OK: case UITYPE_BUTTONBOX_OK:
ui.buttonBox->button(QDialogButtonBox::Ok)->setText(text); ui.buttonBox->button(QDialogButtonBox::Ok)->setText(text);
@ -173,6 +186,23 @@ void GxsGroupDialog::setUiText(UiType uiType, const QString &text)
} }
} }
void GxsGroupDialog::setUiToolTip(UiType uiType, const QString &text)
{
switch (uiType)
{
case UITYPE_KEY_SHARE_CHECKBOX:
ui.pubKeyShare_cb->setToolTip(text);
break;
case UITYPE_ADD_ADMINS_CHECKBOX:
ui.addAdmins_cb->setToolTip(text);
break;
case UITYPE_BUTTONBOX_OK:
ui.buttonBox->button(QDialogButtonBox::Ok)->setToolTip(text);
default:
break;
}
}
void GxsGroupDialog::initMode() void GxsGroupDialog::initMode()
{ {
setAllReadonly(); setAllReadonly();
@ -345,6 +375,9 @@ void GxsGroupDialog::setupVisibility()
ui.publishGroupBox->setVisible(mEnabledFlags & GXS_GROUP_FLAGS_PUBLISHSIGN); ui.publishGroupBox->setVisible(mEnabledFlags & GXS_GROUP_FLAGS_PUBLISHSIGN);
ui.pubKeyShare_cb->setVisible(mEnabledFlags & GXS_GROUP_FLAGS_SHAREKEYS); ui.pubKeyShare_cb->setVisible(mEnabledFlags & GXS_GROUP_FLAGS_SHAREKEYS);
ui.addAdmins_cb->setVisible(mEnabledFlags & GXS_GROUP_FLAGS_ADDADMINS);
ui.label_8->setVisible(mEnabledFlags & GXS_GROUP_FLAGS_ADDADMINS);
ui.moderatorsLabel->setVisible(mEnabledFlags & GXS_GROUP_FLAGS_ADDADMINS);
ui.personalGroupBox->setVisible(mEnabledFlags & GXS_GROUP_FLAGS_PERSONALSIGN); ui.personalGroupBox->setVisible(mEnabledFlags & GXS_GROUP_FLAGS_PERSONALSIGN);
@ -828,6 +861,31 @@ QString GxsGroupDialog::getDescription()
return ui.groupDesc->toPlainText(); return ui.groupDesc->toPlainText();
} }
void GxsGroupDialog::getSelectedModerators(std::set<RsGxsId>& ids)
{
ui.adminsList->selectedIds<RsGxsId,FriendSelectionWidget::IDTYPE_GXS>(ids, true);
}
void GxsGroupDialog::setSelectedModerators(const std::set<RsGxsId>& ids)
{
ui.adminsList->setSelectedIds<RsGxsId,FriendSelectionWidget::IDTYPE_GXS>(ids, false);
QString moderatorsListString ;
RsIdentityDetails det;
for(auto it(ids.begin());it!=ids.end();++it)
{
rsIdentity->getIdDetails(*it,det);
if(!moderatorsListString.isNull())
moderatorsListString += ", " ;
moderatorsListString += det.mNickname.empty()?("[Unknown]"):QString::fromStdString(det.mNickname) ;
}
ui.moderatorsLabel->setText(moderatorsListString);
}
/*********************************************************************************** /***********************************************************************************
Share Lists. Share Lists.
***********************************************************************************/ ***********************************************************************************/
@ -837,6 +895,20 @@ void GxsGroupDialog::sendShareList(std::string /*groupId*/)
close(); close();
} }
void GxsGroupDialog::setAdminsList()
{
if (ui.addAdmins_cb->isChecked())
{
//this->resize(this->size().width() + ui.contactsdockWidget->size().width(), this->size().height());
ui.adminsList->show();
}
else
{ // hide share widget
ui.adminsList->hide();
//this->resize(this->size().width() - ui.contactsdockWidget->size().width(), this->size().height());
}
}
void GxsGroupDialog::setShareList() void GxsGroupDialog::setShareList()
{ {
if (ui.pubKeyShare_cb->isChecked()) { if (ui.pubKeyShare_cb->isChecked()) {

View file

@ -55,44 +55,45 @@ public:
/*** Group flags affect what is visually enabled that gets input into the grpMeta ***/ /*** Group flags affect what is visually enabled that gets input into the grpMeta ***/
#define GXS_GROUP_FLAGS_NAME 0x00000001 #define GXS_GROUP_FLAGS_NAME 0x00000001
#define GXS_GROUP_FLAGS_ICON 0x00000002 #define GXS_GROUP_FLAGS_ICON 0x00000002
#define GXS_GROUP_FLAGS_DESCRIPTION 0x00000004 #define GXS_GROUP_FLAGS_DESCRIPTION 0x00000004
#define GXS_GROUP_FLAGS_DISTRIBUTION 0x00000008 #define GXS_GROUP_FLAGS_DISTRIBUTION 0x00000008
#define GXS_GROUP_FLAGS_PUBLISHSIGN 0x00000010 #define GXS_GROUP_FLAGS_PUBLISHSIGN 0x00000010
#define GXS_GROUP_FLAGS_SHAREKEYS 0x00000020 #define GXS_GROUP_FLAGS_SHAREKEYS 0x00000020
#define GXS_GROUP_FLAGS_PERSONALSIGN 0x00000040 #define GXS_GROUP_FLAGS_PERSONALSIGN 0x00000040
#define GXS_GROUP_FLAGS_COMMENTS 0x00000080 #define GXS_GROUP_FLAGS_COMMENTS 0x00000080
#define GXS_GROUP_FLAGS_EXTRA 0x00000100 #define GXS_GROUP_FLAGS_EXTRA 0x00000100
#define GXS_GROUP_FLAGS_ANTI_SPAM 0x00000200 #define GXS_GROUP_FLAGS_ANTI_SPAM 0x00000200
#define GXS_GROUP_FLAGS_ADDADMINS 0x00000400
/*** Default flags are used to determine privacy of group, signatures required *** /*** Default flags are used to determine privacy of group, signatures required ***
*** whether publish or id and whether comments are allowed or not ***/ *** whether publish or id and whether comments are allowed or not ***/
#define GXS_GROUP_DEFAULTS_DISTRIB_MASK 0x0000000f #define GXS_GROUP_DEFAULTS_DISTRIB_MASK 0x0000000f
#define GXS_GROUP_DEFAULTS_PUBLISH_MASK 0x000000f0 #define GXS_GROUP_DEFAULTS_PUBLISH_MASK 0x000000f0
#define GXS_GROUP_DEFAULTS_PERSONAL_MASK 0x00000f00 #define GXS_GROUP_DEFAULTS_PERSONAL_MASK 0x00000f00
#define GXS_GROUP_DEFAULTS_COMMENTS_MASK 0x0000f000 #define GXS_GROUP_DEFAULTS_COMMENTS_MASK 0x0000f000
#define GXS_GROUP_DEFAULTS_DISTRIB_PUBLIC 0x00000001 #define GXS_GROUP_DEFAULTS_DISTRIB_PUBLIC 0x00000001
#define GXS_GROUP_DEFAULTS_DISTRIB_GROUP 0x00000002 #define GXS_GROUP_DEFAULTS_DISTRIB_GROUP 0x00000002
#define GXS_GROUP_DEFAULTS_DISTRIB_LOCAL 0x00000004 #define GXS_GROUP_DEFAULTS_DISTRIB_LOCAL 0x00000004
#define GXS_GROUP_DEFAULTS_PUBLISH_OPEN 0x00000010 #define GXS_GROUP_DEFAULTS_PUBLISH_OPEN 0x00000010
#define GXS_GROUP_DEFAULTS_PUBLISH_THREADS 0x00000020 #define GXS_GROUP_DEFAULTS_PUBLISH_THREADS 0x00000020
#define GXS_GROUP_DEFAULTS_PUBLISH_REQUIRED 0x00000040 #define GXS_GROUP_DEFAULTS_PUBLISH_REQUIRED 0x00000040
#define GXS_GROUP_DEFAULTS_PUBLISH_ENCRYPTED 0x00000080 #define GXS_GROUP_DEFAULTS_PUBLISH_ENCRYPTED 0x00000080
#define GXS_GROUP_DEFAULTS_PERSONAL_PGP 0x00000100 #define GXS_GROUP_DEFAULTS_PERSONAL_PGP 0x00000100
#define GXS_GROUP_DEFAULTS_PERSONAL_REQUIRED 0x00000200 #define GXS_GROUP_DEFAULTS_PERSONAL_REQUIRED 0x00000200
#define GXS_GROUP_DEFAULTS_PERSONAL_IFNOPUB 0x00000400 #define GXS_GROUP_DEFAULTS_PERSONAL_IFNOPUB 0x00000400
#define GXS_GROUP_DEFAULTS_COMMENTS_YES 0x00001000 #define GXS_GROUP_DEFAULTS_COMMENTS_YES 0x00001000
#define GXS_GROUP_DEFAULTS_COMMENTS_NO 0x00002000 #define GXS_GROUP_DEFAULTS_COMMENTS_NO 0x00002000
#define GXS_GROUP_DEFAULTS_ANTISPAM_FAVOR_PGP 0x00100000 #define GXS_GROUP_DEFAULTS_ANTISPAM_FAVOR_PGP 0x00100000
#define GXS_GROUP_DEFAULTS_ANTISPAM_TRACK 0x00200000 #define GXS_GROUP_DEFAULTS_ANTISPAM_TRACK 0x00200000
#define GXS_GROUP_DEFAULTS_ANTISPAM_FAVOR_PGP_KNOWN 0x00400000 #define GXS_GROUP_DEFAULTS_ANTISPAM_FAVOR_PGP_KNOWN 0x00400000
/*! /*!
* The aim of this dialog is to be convenient to encapsulate group * The aim of this dialog is to be convenient to encapsulate group
@ -121,6 +122,7 @@ public:
enum UiType { enum UiType {
UITYPE_SERVICE_HEADER, UITYPE_SERVICE_HEADER,
UITYPE_KEY_SHARE_CHECKBOX, UITYPE_KEY_SHARE_CHECKBOX,
UITYPE_ADD_ADMINS_CHECKBOX,
UITYPE_CONTACTS_DOCK, UITYPE_CONTACTS_DOCK,
UITYPE_BUTTONBOX_OK UITYPE_BUTTONBOX_OK
}; };
@ -172,7 +174,14 @@ protected:
virtual QPixmap serviceImage() = 0; virtual QPixmap serviceImage() = 0;
virtual QIcon serviceWindowIcon(); virtual QIcon serviceWindowIcon();
void setUiText(UiType uiType, const QString &text); /*!
* \brief setUiToolTip/setUiText
* Sets the text and tooltip of some parts of the UI
* \param uiType widget to set
* \param text text to set
*/
void setUiToolTip(UiType uiType, const QString &text);
void setUiText (UiType uiType, const QString &text);
/*! /*!
* It is up to the service to do the actual group creation * It is up to the service to do the actual group creation
@ -219,12 +228,20 @@ protected:
*/ */
QString getDescription(); QString getDescription();
/*!
* \brief getSelectedModerators
* Returns the set of ids that hve been selected as moderators.
*/
void getSelectedModerators(std::set<RsGxsId>& ids);
void setSelectedModerators(const std::set<RsGxsId>& ids);
private slots: private slots:
/* actions to take.... */ /* actions to take.... */
void cancelDialog(); void cancelDialog();
// set private forum key share list // set private forum key share list
void setShareList(); void setShareList();
void setAdminsList();
void updateCircleOptions(); void updateCircleOptions();

View file

@ -7,7 +7,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1237</width> <width>1237</width>
<height>1145</height> <height>1189</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -44,7 +44,7 @@
<enum>QFrame::Raised</enum> <enum>QFrame::Raised</enum>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>0</number> <number>1</number>
</property> </property>
<widget class="QWidget" name="createmode"> <widget class="QWidget" name="createmode">
<layout class="QGridLayout" name="gridLayout_8"> <layout class="QGridLayout" name="gridLayout_8">
@ -172,13 +172,23 @@
<string>Will be used to send feedback</string> <string>Will be used to send feedback</string>
</property> </property>
<property name="text"> <property name="text">
<string>Owner:</string> <string>Contact:</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="GxsIdChooser" name="idChooser"/> <widget class="GxsIdChooser" name="idChooser"/>
</item> </item>
<item>
<widget class="QCheckBox" name="addAdmins_cb">
<property name="toolTip">
<string/>
</property>
<property name="text">
<string>Add moderators</string>
</property>
</widget>
</item>
<item> <item>
<widget class="QCheckBox" name="pubKeyShare_cb"> <widget class="QCheckBox" name="pubKeyShare_cb">
<property name="toolTip"> <property name="toolTip">
@ -196,102 +206,6 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="0" column="1">
<widget class="QDockWidget" name="contactsdockWidget">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>300</width>
<height>524287</height>
</size>
</property>
<property name="sizeIncrement">
<size>
<width>220</width>
<height>0</height>
</size>
</property>
<property name="baseSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="toolTip">
<string>check peers you would like to share private publish key with</string>
</property>
<property name="floating">
<bool>false</bool>
</property>
<property name="features">
<set>QDockWidget::NoDockWidgetFeatures</set>
</property>
<property name="windowTitle">
<string>Share Key With</string>
</property>
<widget class="QWidget" name="dockWidgetContents">
<layout class="QGridLayout" name="_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="FriendSelectionWidget" name="keyShareList" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>4</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>300</width>
<height>16777215</height>
</size>
</property>
<property name="sizeIncrement">
<size>
<width>220</width>
<height>0</height>
</size>
</property>
<property name="baseSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QGroupBox" name="distribGroupBox"> <widget class="QGroupBox" name="distribGroupBox">
<property name="sizePolicy"> <property name="sizePolicy">
@ -435,7 +349,7 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="2" column="0"> <item row="4" column="0">
<widget class="QGroupBox" name="groupBox"> <widget class="QGroupBox" name="groupBox">
<property name="title"> <property name="title">
<string>Description</string> <string>Description</string>
@ -447,7 +361,7 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="3" column="0"> <item row="5" column="0">
<widget class="QGroupBox" name="publishGroupBox"> <widget class="QGroupBox" name="publishGroupBox">
<property name="title"> <property name="title">
<string>Publish Signatures</string> <string>Publish Signatures</string>
@ -512,7 +426,7 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="4" column="0"> <item row="6" column="0">
<widget class="QGroupBox" name="personalGroupBox"> <widget class="QGroupBox" name="personalGroupBox">
<property name="title"> <property name="title">
<string>Personal Signatures</string> <string>Personal Signatures</string>
@ -555,7 +469,7 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="5" column="0"> <item row="7" column="0">
<widget class="QGroupBox" name="commentGroupBox"> <widget class="QGroupBox" name="commentGroupBox">
<property name="title"> <property name="title">
<string>Comments</string> <string>Comments</string>
@ -606,7 +520,7 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="6" column="0"> <item row="8" column="0">
<widget class="QGroupBox" name="spamProtection_GB"> <widget class="QGroupBox" name="spamProtection_GB">
<property name="title"> <property name="title">
<string>Spam-protection</string> <string>Spam-protection</string>
@ -657,7 +571,7 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="7" column="0" colspan="2"> <item row="9" column="0" colspan="2">
<widget class="QFrame" name="extraFrame"> <widget class="QFrame" name="extraFrame">
<property name="frameShape"> <property name="frameShape">
<enum>QFrame::StyledPanel</enum> <enum>QFrame::StyledPanel</enum>
@ -667,6 +581,12 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0">
<widget class="FriendSelectionWidget" name="adminsList" native="true"/>
</item>
<item row="3" column="0">
<widget class="FriendSelectionWidget" name="shareKeyList" native="true"/>
</item>
</layout> </layout>
</widget> </widget>
<widget class="QWidget" name="showmode"> <widget class="QWidget" name="showmode">
@ -676,150 +596,168 @@
<property name="title"> <property name="title">
<string>Info</string> <string>Info</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout_2"> <layout class="QVBoxLayout" name="verticalLayout">
<item row="0" column="0"> <item>
<widget class="QLabel" name="label_3"> <layout class="QGridLayout" name="gridLayout_2">
<property name="text"> <item row="9" column="0">
<string>Name</string> <widget class="QLabel" name="label_8">
</property> <property name="text">
</widget> <string>Moderators:</string>
</item> </property>
<item row="0" column="1"> </widget>
<widget class="QLineEdit" name="nameline"> </item>
<property name="readOnly"> <item row="7" column="2">
<bool>true</bool> <layout class="QHBoxLayout" name="horizontalLayout_10">
</property> <item>
</widget> <widget class="QLabel" name="distributionValueLabel">
</item> <property name="text">
<item row="1" column="0"> <string>TextLabel</string>
<widget class="QLabel" name="label_10"> </property>
<property name="text"> </widget>
<string>Popularity</string> </item>
</property> <item>
</widget> <widget class="GxsCircleChooser" name="distributionCircleComboBox">
</item> <property name="sizePolicy">
<item row="1" column="1"> <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<widget class="QLineEdit" name="popline"> <horstretch>0</horstretch>
<property name="readOnly"> <verstretch>0</verstretch>
<bool>true</bool> </sizepolicy>
</property> </property>
</widget> </widget>
</item> </item>
<item row="2" column="0"> </layout>
<widget class="QLabel" name="label_6"> </item>
<property name="text"> <item row="2" column="2">
<string>Posts</string> <widget class="QLineEdit" name="postsline">
</property> <property name="text">
</widget> <string/>
</item> </property>
<item row="2" column="1"> <property name="readOnly">
<widget class="QLineEdit" name="postsline"> <bool>true</bool>
<property name="text"> </property>
<string/> </widget>
</property> </item>
<property name="readOnly"> <item row="1" column="2">
<bool>true</bool> <widget class="QLineEdit" name="popline">
</property> <property name="readOnly">
</widget> <bool>true</bool>
</item> </property>
<item row="3" column="0"> </widget>
<widget class="QLabel" name="label_11"> </item>
<property name="text"> <item row="2" column="0">
<string>Last Post</string> <widget class="QLabel" name="label_6">
</property> <property name="text">
</widget> <string>Posts</string>
</item> </property>
<item row="3" column="1"> </widget>
<widget class="QLineEdit" name="lastpostline"> </item>
<property name="readOnly"> <item row="4" column="2">
<bool>true</bool> <widget class="GxsIdLabel" name="authorLabel">
</property> <property name="text">
</widget> <string>GxsIdLabel</string>
</item> </property>
<item row="4" column="0"> </widget>
<widget class="QLabel" name="label_7"> </item>
<property name="text"> <item row="5" column="0">
<string>Author</string> <widget class="QLabel" name="label_4">
</property> <property name="text">
</widget> <string>ID</string>
</item> </property>
<item row="4" column="1"> </widget>
<widget class="GxsIdLabel" name="authorLabel"> </item>
<property name="text"> <item row="3" column="2">
<string>GxsIdLabel</string> <widget class="QLineEdit" name="lastpostline">
</property> <property name="readOnly">
</widget> <bool>true</bool>
</item> </property>
<item row="5" column="0"> </widget>
<widget class="QLabel" name="label_4"> </item>
<property name="text"> <item row="4" column="0">
<string>ID</string> <widget class="QLabel" name="label_7">
</property> <property name="text">
</widget> <string>Author</string>
</item> </property>
<item row="5" column="1"> </widget>
<widget class="QLineEdit" name="IDline"> </item>
<property name="readOnly"> <item row="8" column="2">
<bool>true</bool> <widget class="QLabel" name="antiSpamValueLabel">
</property>
</widget>
</item>
<item row="6" column="0" rowspan="2">
<widget class="QLabel" name="commentsLabel">
<property name="text">
<string>Comments:</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QLabel" name="commentsValueLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="7" column="1" rowspan="2">
<layout class="QHBoxLayout" name="horizontalLayout_10">
<item>
<widget class="QLabel" name="distributionValueLabel">
<property name="text"> <property name="text">
<string>TextLabel</string> <string>TextLabel</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item row="5" column="2">
<widget class="GxsCircleChooser" name="distributionCircleComboBox"> <widget class="QLineEdit" name="IDline">
<property name="sizePolicy"> <property name="readOnly">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed"> <bool>true</bool>
<horstretch>0</horstretch> </property>
<verstretch>0</verstretch> </widget>
</sizepolicy> </item>
<item row="6" column="0">
<widget class="QLabel" name="commentsLabel">
<property name="text">
<string>Comments:</string>
</property>
</widget>
</item>
<item row="6" column="2">
<widget class="QLabel" name="commentsValueLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="distributionLabel">
<property name="text">
<string>Distribution:</string>
</property>
</widget>
</item>
<item row="8" column="0">
<widget class="QLabel" name="antiSpamLabel">
<property name="text">
<string>Anti Spam:</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_11">
<property name="text">
<string>Last Post</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Name</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Popularity</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLineEdit" name="nameline">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="9" column="2">
<widget class="QLabel" name="moderatorsLabel">
<property name="text">
<string>TextLabel</string>
</property> </property>
</widget> </widget>
</item> </item>
</layout> </layout>
</item> </item>
<item row="8" column="0">
<widget class="QLabel" name="distributionLabel">
<property name="text">
<string>Distribution:</string>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="antiSpamLabel">
<property name="text">
<string>Anti Spam:</string>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QLabel" name="antiSpamValueLabel">
<property name="text">
<string>TextLabel</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</item> </item>

View file

@ -870,7 +870,7 @@ QString GxsIdDetails::getFailedText(const RsGxsId &id)
QString GxsIdDetails::getEmptyIdText() QString GxsIdDetails::getEmptyIdText()
{ {
return QApplication::translate("GxsIdDetails", "No Signature"); return QApplication::translate("GxsIdDetails", "[None]");
} }
QString GxsIdDetails::getNameForType(GxsIdDetailsType type, const RsIdentityDetails &details) QString GxsIdDetails::getNameForType(GxsIdDetailsType type, const RsIdentityDetails &details)

View file

@ -34,7 +34,7 @@ const uint32_t ChannelCreateEnabledFlags = (
GXS_GROUP_FLAGS_DESCRIPTION | GXS_GROUP_FLAGS_DESCRIPTION |
GXS_GROUP_FLAGS_DISTRIBUTION | GXS_GROUP_FLAGS_DISTRIBUTION |
// GXS_GROUP_FLAGS_PUBLISHSIGN | // GXS_GROUP_FLAGS_PUBLISHSIGN |
GXS_GROUP_FLAGS_SHAREKEYS | // GXS_GROUP_FLAGS_SHAREKEYS | // disabled because the UI doesn't handle it, so no need to show the disabled button. The user can do it in a second step from the channel menu.
// GXS_GROUP_FLAGS_PERSONALSIGN | // GXS_GROUP_FLAGS_PERSONALSIGN |
GXS_GROUP_FLAGS_COMMENTS | GXS_GROUP_FLAGS_COMMENTS |
0); 0);
@ -75,19 +75,19 @@ void GxsChannelGroupDialog::initUi()
{ {
case MODE_CREATE: case MODE_CREATE:
setUiText(UITYPE_SERVICE_HEADER, tr("Create New Channel")); setUiText(UITYPE_SERVICE_HEADER, tr("Create New Channel"));
setUiText(UITYPE_BUTTONBOX_OK, tr("Create")); setUiText(UITYPE_BUTTONBOX_OK, tr("Create"));
break; break;
case MODE_SHOW: case MODE_SHOW:
setUiText(UITYPE_SERVICE_HEADER, tr("Channel")); setUiText(UITYPE_SERVICE_HEADER, tr("Channel"));
break; break;
case MODE_EDIT: case MODE_EDIT:
setUiText(UITYPE_SERVICE_HEADER, tr("Edit Channel")); setUiText(UITYPE_SERVICE_HEADER, tr("Edit Channel"));
setUiText(UITYPE_BUTTONBOX_OK, tr("Update Channel")); setUiText(UITYPE_BUTTONBOX_OK, tr("Update Channel"));
break; break;
} }
setUiText(UITYPE_KEY_SHARE_CHECKBOX, tr("Add Channel Admins")); setUiText(UITYPE_KEY_SHARE_CHECKBOX, tr("Add Channel Admins"));
setUiText(UITYPE_CONTACTS_DOCK, tr("Select Channel Admins")); setUiText(UITYPE_CONTACTS_DOCK, tr("Select Channel Admins"));
} }
QPixmap GxsChannelGroupDialog::serviceImage() QPixmap GxsChannelGroupDialog::serviceImage()

View file

@ -50,8 +50,9 @@
//#define ENABLE_GENERATE //#define ENABLE_GENERATE
/** Constructor */ /** Constructor */
CreateGxsForumMsg::CreateGxsForumMsg(const RsGxsGroupId &fId, const RsGxsMessageId &pId,const RsGxsMessageId& mOId,const RsGxsId& posterId) CreateGxsForumMsg::CreateGxsForumMsg(const RsGxsGroupId &fId, const RsGxsMessageId &pId, const RsGxsMessageId& mOId, const RsGxsId& posterId, bool isModerating)
: QDialog(NULL, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint), mForumId(fId), mParentId(pId), mOrigMsgId(mOId),mPosterId(posterId) : QDialog(NULL, Qt::WindowSystemMenuHint | Qt::WindowTitleHint | Qt::WindowMinMaxButtonsHint | Qt::WindowCloseButtonHint),
mForumId(fId), mParentId(pId), mOrigMsgId(mOId),mPosterId(posterId),mIsModerating(isModerating)
{ {
/* Invoke the Qt Designer generated object setup routine */ /* Invoke the Qt Designer generated object setup routine */
ui.setupUi(this); ui.setupUi(this);
@ -355,7 +356,9 @@ void CreateGxsForumMsg::createMsg()
msg.mMeta.mGroupId = mForumId; msg.mMeta.mGroupId = mForumId;
msg.mMeta.mParentId = mParentId; msg.mMeta.mParentId = mParentId;
msg.mMeta.mOrigMsgId = mOrigMsgId; msg.mMeta.mOrigMsgId = mOrigMsgId;
msg.mMeta.mMsgFlags = mIsModerating?RS_GXS_FORUM_MSG_FLAGS_MODERATED : 0;
msg.mMeta.mMsgId.clear() ; msg.mMeta.mMsgId.clear() ;
if (mParentMsgLoaded) { if (mParentMsgLoaded) {
msg.mMeta.mThreadId = mParentMsg.mMeta.mThreadId; msg.mMeta.mThreadId = mParentMsg.mMeta.mThreadId;
}//if (mParentMsgLoaded) }//if (mParentMsgLoaded)

View file

@ -36,7 +36,7 @@ class CreateGxsForumMsg : public QDialog, public TokenResponse
Q_OBJECT Q_OBJECT
public: public:
CreateGxsForumMsg(const RsGxsGroupId &fId, const RsGxsMessageId &pId, const RsGxsMessageId &moId, const RsGxsId &posterId = RsGxsId()); CreateGxsForumMsg(const RsGxsGroupId &fId, const RsGxsMessageId &pId, const RsGxsMessageId &moId, const RsGxsId &posterId = RsGxsId(),bool isModerating=false);
~CreateGxsForumMsg(); ~CreateGxsForumMsg();
void newMsg(); /* cleanup */ void newMsg(); /* cleanup */
@ -75,6 +75,7 @@ private:
bool mOrigMsgLoaded; bool mOrigMsgLoaded;
bool mForumMetaLoaded; bool mForumMetaLoaded;
bool mForumCircleLoaded ; bool mForumCircleLoaded ;
bool mIsModerating; // means that the msg has a orig author Id that is not the Id of the author
RsGxsForumMsg mParentMsg; RsGxsForumMsg mParentMsg;
RsGxsForumMsg mOrigMsg; RsGxsForumMsg mOrigMsg;

View file

@ -27,12 +27,13 @@
// To start with we only have open forums - with distribution controls. // To start with we only have open forums - with distribution controls.
const uint32_t ForumCreateEnabledFlags = ( const uint32_t ForumCreateEnabledFlags = (
GXS_GROUP_FLAGS_NAME | GXS_GROUP_FLAGS_NAME |
// GXS_GROUP_FLAGS_ICON | // GXS_GROUP_FLAGS_ICON |
GXS_GROUP_FLAGS_DESCRIPTION | GXS_GROUP_FLAGS_DESCRIPTION |
GXS_GROUP_FLAGS_DISTRIBUTION | GXS_GROUP_FLAGS_DISTRIBUTION |
// GXS_GROUP_FLAGS_PUBLISHSIGN | // GXS_GROUP_FLAGS_PUBLISHSIGN|
GXS_GROUP_FLAGS_SHAREKEYS | // GXS_GROUP_FLAGS_SHAREKEYS |
GXS_GROUP_FLAGS_ADDADMINS |
GXS_GROUP_FLAGS_ANTI_SPAM | GXS_GROUP_FLAGS_ANTI_SPAM |
// GXS_GROUP_FLAGS_PERSONALSIGN | // GXS_GROUP_FLAGS_PERSONALSIGN |
// GXS_GROUP_FLAGS_COMMENTS | // GXS_GROUP_FLAGS_COMMENTS |
@ -42,17 +43,17 @@ const uint32_t ForumCreateDefaultsFlags = ( GXS_GROUP_DEFAULTS_DISTRIB_PUBLIC
//GXS_GROUP_DEFAULTS_DISTRIB_GROUP | //GXS_GROUP_DEFAULTS_DISTRIB_GROUP |
//GXS_GROUP_DEFAULTS_DISTRIB_LOCAL | //GXS_GROUP_DEFAULTS_DISTRIB_LOCAL |
GXS_GROUP_DEFAULTS_PUBLISH_OPEN | GXS_GROUP_DEFAULTS_PUBLISH_OPEN |
//GXS_GROUP_DEFAULTS_PUBLISH_THREADS | //GXS_GROUP_DEFAULTS_PUBLISH_THREADS |
//GXS_GROUP_DEFAULTS_PUBLISH_REQUIRED | //GXS_GROUP_DEFAULTS_PUBLISH_REQUIRED |
//GXS_GROUP_DEFAULTS_PUBLISH_ENCRYPTED | //GXS_GROUP_DEFAULTS_PUBLISH_ENCRYPTED |
//GXS_GROUP_DEFAULTS_PERSONAL_PGP | //GXS_GROUP_DEFAULTS_PERSONAL_PGP |
GXS_GROUP_DEFAULTS_PERSONAL_REQUIRED | GXS_GROUP_DEFAULTS_PERSONAL_REQUIRED |
//GXS_GROUP_DEFAULTS_PERSONAL_IFNOPUB | //GXS_GROUP_DEFAULTS_PERSONAL_IFNOPUB |
//GXS_GROUP_DEFAULTS_COMMENTS_YES | //GXS_GROUP_DEFAULTS_COMMENTS_YES |
GXS_GROUP_DEFAULTS_COMMENTS_NO | GXS_GROUP_DEFAULTS_COMMENTS_NO |
0); 0);
const uint32_t ForumEditEnabledFlags = ForumCreateEnabledFlags; const uint32_t ForumEditEnabledFlags = ForumCreateEnabledFlags;
@ -61,11 +62,13 @@ const uint32_t ForumEditDefaultsFlags = ForumCreateDefaultsFlags;
GxsForumGroupDialog::GxsForumGroupDialog(TokenQueue *tokenQueue, QWidget *parent) GxsForumGroupDialog::GxsForumGroupDialog(TokenQueue *tokenQueue, QWidget *parent)
: GxsGroupDialog(tokenQueue, ForumCreateEnabledFlags, ForumCreateDefaultsFlags, parent) : GxsGroupDialog(tokenQueue, ForumCreateEnabledFlags, ForumCreateDefaultsFlags, parent)
{ {
ui.pubKeyShare_cb->setEnabled(true) ;
} }
GxsForumGroupDialog::GxsForumGroupDialog(TokenQueue *tokenExternalQueue, RsTokenService *tokenService, Mode mode, RsGxsGroupId groupId, QWidget *parent) GxsForumGroupDialog::GxsForumGroupDialog(TokenQueue *tokenExternalQueue, RsTokenService *tokenService, Mode mode, RsGxsGroupId groupId, QWidget *parent)
: GxsGroupDialog(tokenExternalQueue, tokenService, mode, groupId, ForumEditEnabledFlags, ForumEditDefaultsFlags, parent) : GxsGroupDialog(tokenExternalQueue, tokenService, mode, groupId, ForumEditEnabledFlags, ForumEditDefaultsFlags, parent)
{ {
ui.pubKeyShare_cb->setEnabled(true) ;
} }
void GxsForumGroupDialog::initUi() void GxsForumGroupDialog::initUi()
@ -85,8 +88,10 @@ void GxsForumGroupDialog::initUi()
break; break;
} }
setUiText(UITYPE_KEY_SHARE_CHECKBOX, tr("Add Forum Admins")); setUiToolTip(UITYPE_ADD_ADMINS_CHECKBOX,tr("Forum moderators can edit/delete/pinup others posts"));
setUiText(UITYPE_CONTACTS_DOCK, tr("Select Forum Admins"));
//setUiText(UITYPE_KEY_SHARE_CHECKBOX, tr("Add Forum Admins"));
//setUiText(UITYPE_CONTACTS_DOCK, tr("Select Forum Admins"));
} }
QPixmap GxsForumGroupDialog::serviceImage() QPixmap GxsForumGroupDialog::serviceImage()
@ -100,6 +105,7 @@ bool GxsForumGroupDialog::service_CreateGroup(uint32_t &token, const RsGroupMeta
RsGxsForumGroup grp; RsGxsForumGroup grp;
grp.mMeta = meta; grp.mMeta = meta;
grp.mDescription = getDescription().toUtf8().constData(); grp.mDescription = getDescription().toUtf8().constData();
getSelectedModerators(grp.mAdminList.ids);
rsGxsForums->createGroup(token, grp); rsGxsForums->createGroup(token, grp);
return true; return true;
@ -107,10 +113,15 @@ bool GxsForumGroupDialog::service_CreateGroup(uint32_t &token, const RsGroupMeta
bool GxsForumGroupDialog::service_EditGroup(uint32_t &token, RsGroupMetaData &editedMeta) bool GxsForumGroupDialog::service_EditGroup(uint32_t &token, RsGroupMetaData &editedMeta)
{ {
RsGxsForumGroup grp; RsGxsForumGroup grp(mGroupData); // start again from cached information. That allows to keep the pinned posts for instance.
// now replace data by locally edited/changed information
grp.mMeta = editedMeta; grp.mMeta = editedMeta;
grp.mDescription = getDescription().toUtf8().constData(); grp.mDescription = getDescription().toUtf8().constData();
getSelectedModerators(grp.mAdminList.ids);
std::cerr << "GxsForumGroupDialog::service_EditGroup() submitting changes"; std::cerr << "GxsForumGroupDialog::service_EditGroup() submitting changes";
std::cerr << std::endl; std::cerr << std::endl;
@ -124,6 +135,7 @@ bool GxsForumGroupDialog::service_loadGroup(uint32_t token, Mode /*mode*/, RsGro
std::cerr << std::endl; std::cerr << std::endl;
std::vector<RsGxsForumGroup> groups; std::vector<RsGxsForumGroup> groups;
if (!rsGxsForums->getGroupData(token, groups)) if (!rsGxsForums->getGroupData(token, groups))
{ {
std::cerr << "GxsForumGroupDialog::service_loadGroup() Error getting GroupData"; std::cerr << "GxsForumGroupDialog::service_loadGroup() Error getting GroupData";
@ -141,8 +153,16 @@ bool GxsForumGroupDialog::service_loadGroup(uint32_t token, Mode /*mode*/, RsGro
std::cerr << "GxsForumsGroupDialog::service_loadGroup() Unfinished Loading"; std::cerr << "GxsForumsGroupDialog::service_loadGroup() Unfinished Loading";
std::cerr << std::endl; std::cerr << std::endl;
// Information handled by GxsGroupDialog. description should rather be handled here in the service part!
groupMetaData = groups[0].mMeta; groupMetaData = groups[0].mMeta;
description = QString::fromUtf8(groups[0].mDescription.c_str()); description = QString::fromUtf8(groups[0].mDescription.c_str());
// Local information. Description should be handled here.
setSelectedModerators(groups[0].mAdminList.ids);
mGroupData = groups[0]; // keeps the private information
return true; return true;
} }

View file

@ -39,6 +39,9 @@ protected:
virtual bool service_CreateGroup(uint32_t &token, const RsGroupMetaData &meta); virtual bool service_CreateGroup(uint32_t &token, const RsGroupMetaData &meta);
virtual bool service_loadGroup(uint32_t token, Mode mode, RsGroupMetaData& groupMetaData, QString &description); virtual bool service_loadGroup(uint32_t token, Mode mode, RsGroupMetaData& groupMetaData, QString &description);
virtual bool service_EditGroup(uint32_t &token, RsGroupMetaData &editedMeta); virtual bool service_EditGroup(uint32_t &token, RsGroupMetaData &editedMeta);
private:
RsGxsForumGroup mGroupData;
}; };
#endif #endif

View file

@ -88,6 +88,7 @@
#define COLUMN_THREAD_CONTENT 6 #define COLUMN_THREAD_CONTENT 6
#define COLUMN_THREAD_COUNT 7 #define COLUMN_THREAD_COUNT 7
#define COLUMN_THREAD_MSGID 8 #define COLUMN_THREAD_MSGID 8
#define COLUMN_THREAD_NB_COLUMNS 9
#define COLUMN_THREAD_DATA 0 // column for storing the userdata like parentid #define COLUMN_THREAD_DATA 0 // column for storing the userdata like parentid
@ -99,6 +100,7 @@
#define ROLE_THREAD_READCHILDREN Qt::UserRole + 4 #define ROLE_THREAD_READCHILDREN Qt::UserRole + 4
#define ROLE_THREAD_UNREADCHILDREN Qt::UserRole + 5 #define ROLE_THREAD_UNREADCHILDREN Qt::UserRole + 5
#define ROLE_THREAD_SORT Qt::UserRole + 6 #define ROLE_THREAD_SORT Qt::UserRole + 6
#define ROLE_THREAD_PINNED Qt::UserRole + 7
#define ROLE_THREAD_COUNT 4 #define ROLE_THREAD_COUNT 4
@ -246,8 +248,6 @@ GxsForumThreadWidget::GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget
ttheader->resizeSection (COLUMN_THREAD_DISTRIBUTION, 24*f); ttheader->resizeSection (COLUMN_THREAD_DISTRIBUTION, 24*f);
ttheader->resizeSection (COLUMN_THREAD_AUTHOR, 150*f); ttheader->resizeSection (COLUMN_THREAD_AUTHOR, 150*f);
ui->threadTreeWidget->sortItems(COLUMN_THREAD_DATE, Qt::DescendingOrder);
/* Set text of column "Read" to empty - without this the column has a number as header text */ /* Set text of column "Read" to empty - without this the column has a number as header text */
QTreeWidgetItem *headerItem = ui->threadTreeWidget->headerItem(); QTreeWidgetItem *headerItem = ui->threadTreeWidget->headerItem();
headerItem->setText(COLUMN_THREAD_READ, "") ; headerItem->setText(COLUMN_THREAD_READ, "") ;
@ -297,6 +297,7 @@ GxsForumThreadWidget::GxsForumThreadWidget(const RsGxsGroupId &forumId, QWidget
forum visible to all other friends.</p><p>Afterwards you can unsubscribe from the context menu of the forum list at left.</p>")); forum visible to all other friends.</p><p>Afterwards you can unsubscribe from the context menu of the forum list at left.</p>"));
ui->threadTreeWidget->enableColumnCustomize(true); ui->threadTreeWidget->enableColumnCustomize(true);
ui->threadTreeWidget->sortItems(COLUMN_THREAD_DATE, Qt::DescendingOrder);
} }
void GxsForumThreadWidget::blank() void GxsForumThreadWidget::blank()
@ -511,10 +512,15 @@ void GxsForumThreadWidget::threadListCustomPopupMenu(QPoint /*point*/)
} }
QMenu contextMnu(this); QMenu contextMnu(this);
QList<QTreeWidgetItem*> selectedItems = ui->threadTreeWidget->selectedItems();
QAction *editAct = new QAction(QIcon(IMAGE_MESSAGEEDIT), tr("Edit"), &contextMnu); QAction *editAct = new QAction(QIcon(IMAGE_MESSAGEEDIT), tr("Edit"), &contextMnu);
connect(editAct, SIGNAL(triggered()), this, SLOT(editforummessage())); connect(editAct, SIGNAL(triggered()), this, SLOT(editforummessage()));
bool is_pinned = mForumGroup.mPinnedPosts.ids.find(mThreadId) != mForumGroup.mPinnedPosts.ids.end();
QAction *pinUpPostAct = new QAction(QIcon(IMAGE_MESSAGE), (is_pinned?tr("Un-pin this post"):tr("Pin this post up")), &contextMnu);
connect(pinUpPostAct , SIGNAL(triggered()), this, SLOT(togglePinUpPost()));
QAction *replyAct = new QAction(QIcon(IMAGE_MESSAGEREPLY), tr("Reply"), &contextMnu); QAction *replyAct = new QAction(QIcon(IMAGE_MESSAGEREPLY), tr("Reply"), &contextMnu);
connect(replyAct, SIGNAL(triggered()), this, SLOT(replytoforummessage())); connect(replyAct, SIGNAL(triggered()), this, SLOT(replytoforummessage()));
@ -604,16 +610,37 @@ void GxsForumThreadWidget::threadListCustomPopupMenu(QPoint /*point*/)
replyauthorAct->setDisabled (true); replyauthorAct->setDisabled (true);
} }
QList<QTreeWidgetItem*> selectedItems = ui->threadTreeWidget->selectedItems();
if(selectedItems.size() == 1) if(selectedItems.size() == 1)
{ {
QTreeWidgetItem *item = *selectedItems.begin(); QTreeWidgetItem *item = *selectedItems.begin();
GxsIdRSTreeWidgetItem *gxsIdItem = dynamic_cast<GxsIdRSTreeWidgetItem*>(item); GxsIdRSTreeWidgetItem *gxsIdItem = dynamic_cast<GxsIdRSTreeWidgetItem*>(item);
RsGxsId author_id; bool is_pinned = mForumGroup.mPinnedPosts.ids.find( RsGxsMessageId(item->data(COLUMN_THREAD_MSGID,Qt::DisplayRole).toString().toStdString()) ) != mForumGroup.mPinnedPosts.ids.end();
if(gxsIdItem && gxsIdItem->getId(author_id) && rsIdentity->isOwnId(author_id))
contextMnu.addAction(editAct); if(!is_pinned)
{
RsGxsId author_id;
if(gxsIdItem && gxsIdItem->getId(author_id) && rsIdentity->isOwnId(author_id))
contextMnu.addAction(editAct);
else
{
// Go through the list of own ids and see if one of them is a moderator
// TODO: offer to select which moderator ID to use if multiple IDs fit the conditions of the forum
std::list<RsGxsId> own_ids ;
rsIdentity->getOwnIds(own_ids) ;
for(auto it(own_ids.begin());it!=own_ids.end();++it)
if(mForumGroup.mAdminList.ids.find(*it) != mForumGroup.mAdminList.ids.end())
{
contextMnu.addAction(editAct);
break ;
}
}
}
if(IS_GROUP_ADMIN(mSubscribeFlags) && (*selectedItems.begin())->parent() == NULL)
contextMnu.addAction(pinUpPostAct);
} }
contextMnu.addAction(replyAct); contextMnu.addAction(replyAct);
@ -660,6 +687,7 @@ void GxsForumThreadWidget::threadListCustomPopupMenu(QPoint /*point*/)
contextMnu.addAction(showinpeopleAct); contextMnu.addAction(showinpeopleAct);
contextMnu.addAction(replyauthorAct); contextMnu.addAction(replyauthorAct);
} }
} }
contextMnu.exec(QCursor::pos()); contextMnu.exec(QCursor::pos());
@ -783,6 +811,7 @@ void GxsForumThreadWidget::calculateIconsAndFonts(QTreeWidgetItem *item, bool &h
bool isNew = IS_MSG_NEW(status); bool isNew = IS_MSG_NEW(status);
bool unread = IS_MSG_UNREAD(status); bool unread = IS_MSG_UNREAD(status);
bool missing = item->data(COLUMN_THREAD_DATA, ROLE_THREAD_MISSING).toBool(); bool missing = item->data(COLUMN_THREAD_DATA, ROLE_THREAD_MISSING).toBool();
RsGxsMessageId msgId(item->data(COLUMN_THREAD_MSGID,Qt::DisplayRole).toString().toStdString());
// set icon // set icon
if (missing) { if (missing) {
@ -811,6 +840,8 @@ void GxsForumThreadWidget::calculateIconsAndFonts(QTreeWidgetItem *item, bool &h
calculateIconsAndFonts(item->child(index), myReadChilddren, myUnreadChilddren); calculateIconsAndFonts(item->child(index), myReadChilddren, myUnreadChilddren);
} }
bool is_pinned = mForumGroup.mPinnedPosts.ids.find(msgId) != mForumGroup.mPinnedPosts.ids.end();
// set font // set font
for (int i = 0; i < COLUMN_THREAD_COUNT; ++i) { for (int i = 0; i < COLUMN_THREAD_COUNT; ++i) {
QFont qf = item->font(i); QFont qf = item->font(i);
@ -832,6 +863,15 @@ void GxsForumThreadWidget::calculateIconsAndFonts(QTreeWidgetItem *item, bool &h
/* Missing message */ /* Missing message */
item->setForeground(i, textColorMissing()); item->setForeground(i, textColorMissing());
} }
if(is_pinned)
{
qf.setBold(true);
item->setForeground(i, textColorUnread());
item->setData(i,Qt::BackgroundRole, QBrush(QColor(255,200,180))) ;
}
else
item->setData(i,Qt::BackgroundRole, QBrush());
item->setFont(i, qf); item->setFont(i, qf);
} }
@ -968,6 +1008,10 @@ static QString getDurationString(uint32_t days)
else if(IS_GROUP_PGP_AUTHED(tw->mSignFlags)) anti_spam_features1 = tr("Anonymous posts forwarded if reputation is positive"); else if(IS_GROUP_PGP_AUTHED(tw->mSignFlags)) anti_spam_features1 = tr("Anonymous posts forwarded if reputation is positive");
tw->mForumDescription = QString("<b>%1: \t</b>%2<br/>").arg(tr("Forum name"), QString::fromUtf8( group.mMeta.mGroupName.c_str())); tw->mForumDescription = QString("<b>%1: \t</b>%2<br/>").arg(tr("Forum name"), QString::fromUtf8( group.mMeta.mGroupName.c_str()));
tw->mForumDescription += QString("<b>%1: </b>%2<br/>").arg(tr("Description"),
group.mDescription.empty()?
tr("[None]<br/>")
:(QString::fromUtf8(group.mDescription.c_str())+"<br/>"));
tw->mForumDescription += QString("<b>%1: \t</b>%2<br/>").arg(tr("Subscribers")).arg(group.mMeta.mPop); tw->mForumDescription += QString("<b>%1: \t</b>%2<br/>").arg(tr("Subscribers")).arg(group.mMeta.mPop);
tw->mForumDescription += QString("<b>%1: \t</b>%2<br/>").arg(tr("Posts (at neighbor nodes)")).arg(group.mMeta.mVisibleMsgCount); tw->mForumDescription += QString("<b>%1: \t</b>%2<br/>").arg(tr("Posts (at neighbor nodes)")).arg(group.mMeta.mVisibleMsgCount);
if(group.mMeta.mLastPost==0) if(group.mMeta.mLastPost==0)
@ -1014,21 +1058,31 @@ static QString getDurationString(uint32_t days)
} }
tw->mForumDescription += QString("<b>%1: \t</b>%2<br/>").arg(tr("Distribution"), distrib_string); tw->mForumDescription += QString("<b>%1: \t</b>%2<br/>").arg(tr("Distribution"), distrib_string);
tw->mForumDescription += QString("<b>%1: \t</b>%2<br/>").arg(tr("Author"), author); tw->mForumDescription += QString("<b>%1: \t</b>%2<br/>").arg(tr("Contact"), author);
if(!anti_spam_features1.isNull()) if(!anti_spam_features1.isNull())
tw->mForumDescription += QString("<b>%1: \t</b>%2<br/>").arg(tr("Anti-spam")).arg(anti_spam_features1); tw->mForumDescription += QString("<b>%1: \t</b>%2<br/>").arg(tr("Anti-spam")).arg(anti_spam_features1);
tw->mForumDescription += QString("<b>%1: </b><br/><br/>%2").arg(tr("Description"), QString::fromUtf8(group.mDescription.c_str()));
tw->ui->subscribeToolButton->setSubscribed(IS_GROUP_SUBSCRIBED(tw->mSubscribeFlags)); tw->ui->subscribeToolButton->setSubscribed(IS_GROUP_SUBSCRIBED(tw->mSubscribeFlags));
tw->mStateHelper->setWidgetEnabled(tw->ui->newthreadButton, (IS_GROUP_SUBSCRIBED(tw->mSubscribeFlags))); tw->mStateHelper->setWidgetEnabled(tw->ui->newthreadButton, (IS_GROUP_SUBSCRIBED(tw->mSubscribeFlags)));
if (tw->mThreadId.isNull() && !tw->mStateHelper->isLoading(tw->mTokenTypeMessageData)) if(!group.mAdminList.ids.empty())
{ {
//ui->threadTitle->setText(tr("Forum Description")); QString admin_list_str ;
tw->ui->postText->setText(tw->mForumDescription);
for(auto it(group.mAdminList.ids.begin());it!=group.mAdminList.ids.end();++it)
{
RsIdentityDetails det ;
rsIdentity->getIdDetails(*it,det);
admin_list_str += (admin_list_str.isNull()?"":", ") + QString::fromUtf8(det.mNickname.c_str()) ;
}
tw->mForumDescription += QString("<b>%1: </b>%2").arg(tr("Moderators"), admin_list_str);
} }
if (tw->mThreadId.isNull() && !tw->mStateHelper->isLoading(tw->mTokenTypeMessageData))
tw->ui->postText->setText(tw->mForumDescription);
} }
void GxsForumThreadWidget::fillThreadFinished() void GxsForumThreadWidget::fillThreadFinished()
@ -1167,11 +1221,49 @@ void GxsForumThreadWidget::fillThreadStatus(QString text)
ui->progressText->setText(text); ui->progressText->setText(text);
} }
//#define DEBUG_PINNED_POST_SORTING 1
class ForumThreadItem: public GxsIdRSTreeWidgetItem
{
public:
ForumThreadItem(QHeaderView *header,const RSTreeWidgetItemCompareRole *compareRole, uint32_t icon_mask,QTreeWidget *parent = NULL)
: GxsIdRSTreeWidgetItem(compareRole,icon_mask,parent), m_header(header) {}
bool operator<(const QTreeWidgetItem& other) const
{
bool left_is_not_pinned = ! data(COLUMN_THREAD_DATE,ROLE_THREAD_PINNED).toBool();
bool right_is_not_pinned = !other.data(COLUMN_THREAD_DATE,ROLE_THREAD_PINNED).toBool();
#ifdef DEBUG_PINNED_POST_SORTING
std::cerr << "Comparing item date \"" << data(COLUMN_THREAD_DATE,Qt::DisplayRole).toString().toStdString() << "\" ("
<< data(COLUMN_THREAD_DATE,ROLE_THREAD_SORT).toUInt() << ", \"" << data(COLUMN_THREAD_DATE,ROLE_THREAD_SORT).toString().toStdString() << "\" --> " << left_is_not_pinned << ") to \""
<< other.data(COLUMN_THREAD_DATE,Qt::DisplayRole).toString().toStdString() << "\" ("
<< other.data(COLUMN_THREAD_DATE,ROLE_THREAD_SORT).toUInt() << ", \"" << other.data(COLUMN_THREAD_DATE,ROLE_THREAD_SORT).toString().toStdString() << "\" --> " << right_is_not_pinned << ") ";
#endif
if(left_is_not_pinned ^ right_is_not_pinned)
{
#ifdef DEBUG_PINNED_POST_SORTING
std::cerr << "Local: " << ((m_header->sortIndicatorOrder()==Qt::AscendingOrder)?right_is_not_pinned:left_is_not_pinned) << std::endl;
#endif
return (m_header->sortIndicatorOrder()==Qt::AscendingOrder)?right_is_not_pinned:left_is_not_pinned ; // always put pinned posts on top
}
#ifdef DEBUG_PINNED_POST_SORTING
std::cerr << "Remote: " << GxsIdRSTreeWidgetItem::operator<(other) << std::endl;
#endif
return GxsIdRSTreeWidgetItem::operator<(other);
}
private:
QHeaderView *m_header ;
};
QTreeWidgetItem *GxsForumThreadWidget::convertMsgToThreadWidget(const RsGxsForumMsg &msg, bool useChildTS, uint32_t filterColumn, QTreeWidgetItem *parent) QTreeWidgetItem *GxsForumThreadWidget::convertMsgToThreadWidget(const RsGxsForumMsg &msg, bool useChildTS, uint32_t filterColumn, QTreeWidgetItem *parent)
{ {
// Early check for a message that should be hidden because its author // Early check for a message that should be hidden because its author
// is flagged with a bad reputation // is flagged with a bad reputation
bool is_pinned = mForumGroup.mPinnedPosts.ids.find(msg.mMeta.mMsgId) != mForumGroup.mPinnedPosts.ids.end();
uint32_t idflags =0; uint32_t idflags =0;
RsReputations::ReputationLevel reputation_level = rsReputations->overallReputationLevel(msg.mMeta.mAuthorId,&idflags) ; RsReputations::ReputationLevel reputation_level = rsReputations->overallReputationLevel(msg.mMeta.mAuthorId,&idflags) ;
@ -1179,11 +1271,15 @@ QTreeWidgetItem *GxsForumThreadWidget::convertMsgToThreadWidget(const RsGxsForum
redacted = (reputation_level == RsReputations::REPUTATION_LOCALLY_NEGATIVE); redacted = (reputation_level == RsReputations::REPUTATION_LOCALLY_NEGATIVE);
GxsIdRSTreeWidgetItem *item = new GxsIdRSTreeWidgetItem(mThreadCompareRole,GxsIdDetails::ICON_TYPE_AVATAR ); // We use a specific item model for forums in order to handle the post pinning.
GxsIdRSTreeWidgetItem *item = new ForumThreadItem(ui->threadTreeWidget->header(),mThreadCompareRole,GxsIdDetails::ICON_TYPE_AVATAR );
item->moveToThread(ui->threadTreeWidget->thread()); item->moveToThread(ui->threadTreeWidget->thread());
if(redacted) if(redacted)
item->setText(COLUMN_THREAD_TITLE, tr("[ ... Redacted message ... ]")); item->setText(COLUMN_THREAD_TITLE, tr("[ ... Redacted message ... ]"));
else if(is_pinned)
item->setText(COLUMN_THREAD_TITLE, tr("[PINNED] ") + QString::fromUtf8(msg.mMeta.mMsgName.c_str()));
else else
item->setText(COLUMN_THREAD_TITLE, QString::fromUtf8(msg.mMeta.mMsgName.c_str())); item->setText(COLUMN_THREAD_TITLE, QString::fromUtf8(msg.mMeta.mMsgName.c_str()));
@ -1220,30 +1316,42 @@ QTreeWidgetItem *GxsForumThreadWidget::convertMsgToThreadWidget(const RsGxsForum
qtime.setTime_t(msg.mMeta.mPublishTs); qtime.setTime_t(msg.mMeta.mPublishTs);
QString itemText = DateTime::formatDateTime(qtime); QString itemText = DateTime::formatDateTime(qtime);
// This is an attempt to put pinned posts on the top. We should rather use a QSortFilterProxyModel here.
QString itemSort = QString::number(msg.mMeta.mPublishTs);//Don't need to format it as for sort. QString itemSort = QString::number(msg.mMeta.mPublishTs);//Don't need to format it as for sort.
//#define SHOW_COMBINED_DATES 1
if (useChildTS) if (useChildTS)
{ {
for(QTreeWidgetItem *grandParent = parent; grandParent!=NULL; grandParent = grandParent->parent()) for(QTreeWidgetItem *grandParent = parent; grandParent!=NULL; grandParent = grandParent->parent())
{ {
//Update Parent Child TimeStamp //Update Parent Child TimeStamp
QString oldTSText = grandParent->text(COLUMN_THREAD_DATE);
QString oldTSSort = grandParent->data(COLUMN_THREAD_DATE, ROLE_THREAD_SORT).toString(); QString oldTSSort = grandParent->data(COLUMN_THREAD_DATE, ROLE_THREAD_SORT).toString();
QString oldCTSText = oldTSText.split("|").at(0);
QString oldPTSText = oldTSText.contains("|") ? oldTSText.split(" | ").at(1) : oldCTSText;//If first time parent get only its mPublishTs
QString oldCTSSort = oldTSSort.split("|").at(0); QString oldCTSSort = oldTSSort.split("|").at(0);
QString oldPTSSort = oldTSSort.contains("|") ? oldTSSort.split(" | ").at(1) : oldCTSSort; QString oldPTSSort = oldTSSort.contains("|") ? oldTSSort.split(" | ").at(1) : oldCTSSort;
#ifdef SHOW_COMBINED_DATES
QString oldTSText = grandParent->text(COLUMN_THREAD_DATE);
QString oldCTSText = oldTSText.split("|").at(0);
QString oldPTSText = oldTSText.contains("|") ? oldTSText.split(" | ").at(1) : oldCTSText;//If first time parent get only its mPublishTs
#endif
if (oldCTSSort.toDouble() < itemSort.toDouble()) if (oldCTSSort.toDouble() < itemSort.toDouble())
{ {
#ifdef SHOW_COMBINED_DATES
grandParent->setText(COLUMN_THREAD_DATE, DateTime::formatDateTime(qtime) + " | " + oldPTSText); grandParent->setText(COLUMN_THREAD_DATE, DateTime::formatDateTime(qtime) + " | " + oldPTSText);
#endif
grandParent->setData(COLUMN_THREAD_DATE, ROLE_THREAD_SORT, itemSort + " | " + oldPTSSort); grandParent->setData(COLUMN_THREAD_DATE, ROLE_THREAD_SORT, itemSort + " | " + oldPTSSort);
} }
} }
} }
item->setText(COLUMN_THREAD_DATE, itemText); item->setText(COLUMN_THREAD_DATE, itemText);
item->setData(COLUMN_THREAD_DATE, ROLE_THREAD_SORT, itemSort); item->setData(COLUMN_THREAD_DATE,ROLE_THREAD_SORT, itemSort);
if(is_pinned)
item->setData(COLUMN_THREAD_DATE,ROLE_THREAD_PINNED, QVariant(true)); // this is used by the sorting model to put all posts on top
else
item->setData(COLUMN_THREAD_DATE,ROLE_THREAD_PINNED, QVariant(false));
// Set later with GxsIdRSTreeWidgetItem::setId // Set later with GxsIdRSTreeWidgetItem::setId
item->setData(COLUMN_THREAD_DATA, ROLE_THREAD_AUTHOR, QString::fromStdString(msg.mMeta.mAuthorId.toStdString())); item->setData(COLUMN_THREAD_DATA, ROLE_THREAD_AUTHOR, QString::fromStdString(msg.mMeta.mAuthorId.toStdString()));
@ -2080,6 +2188,37 @@ void GxsForumThreadWidget::createmessage()
/* window will destroy itself! */ /* window will destroy itself! */
} }
void GxsForumThreadWidget::togglePinUpPost()
{
if (groupId().isNull() || mThreadId.isNull())
return;
QTreeWidgetItem *item = ui->threadTreeWidget->currentItem();
// normally this method is only called on top level items. We still check it just in case...
if(item->parent() != NULL)
{
std::cerr << "(EE) togglePinUpPost() called on non top level post. This is inconsistent." << std::endl;
return ;
}
QString thread_title = (item != NULL)?item->text(COLUMN_THREAD_TITLE):QString() ;
std::cerr << "Toggling Pin-up state of post " << mThreadId.toStdString() << ": \"" << thread_title.toStdString() << "\"" << std::endl;
if(mForumGroup.mPinnedPosts.ids.find(mThreadId) == mForumGroup.mPinnedPosts.ids.end())
mForumGroup.mPinnedPosts.ids.insert(mThreadId) ;
else
mForumGroup.mPinnedPosts.ids.erase(mThreadId) ;
uint32_t token;
rsGxsForums->updateGroup(token,mForumGroup);
ui->threadTreeWidget->takeTopLevelItem(ui->threadTreeWidget->indexOfTopLevelItem(item)); // forces the re-creation of all posts widgets. A bit extreme. We should rather only delete item above
updateDisplay(true) ;
}
void GxsForumThreadWidget::createthread() void GxsForumThreadWidget::createthread()
{ {
if (groupId().isNull ()) { if (groupId().isNull ()) {
@ -2223,9 +2362,30 @@ void GxsForumThreadWidget::editForumMessageData(const RsGxsForumMsg& msg)
return; return;
} }
// Go through the list of own ids and see if one of them is a moderator
// TODO: offer to select which moderator ID to use if multiple IDs fit the conditions of the forum
RsGxsId moderator_id ;
std::list<RsGxsId> own_ids ;
rsIdentity->getOwnIds(own_ids) ;
for(auto it(own_ids.begin());it!=own_ids.end();++it)
if(mForumGroup.mAdminList.ids.find(*it) != mForumGroup.mAdminList.ids.end())
{
moderator_id = *it;
break;
}
// Check that author is in own ids, if not use the moderator id that was collected among own ids.
bool is_own = false ;
for(auto it(own_ids.begin());it!=own_ids.end() && !is_own;++it)
if(*it == msg.mMeta.mAuthorId)
is_own = true ;
if (!msg.mMeta.mAuthorId.isNull()) if (!msg.mMeta.mAuthorId.isNull())
{ {
CreateGxsForumMsg *cfm = new CreateGxsForumMsg(groupId(), msg.mMeta.mParentId, msg.mMeta.mMsgId, msg.mMeta.mAuthorId); CreateGxsForumMsg *cfm = new CreateGxsForumMsg(groupId(), msg.mMeta.mParentId, msg.mMeta.mMsgId, is_own?(msg.mMeta.mAuthorId):moderator_id,!is_own);
cfm->insertPastedText(QString::fromUtf8(msg.mMsg.c_str())) ; cfm->insertPastedText(QString::fromUtf8(msg.mMsg.c_str())) ;
cfm->show(); cfm->show();

View file

@ -108,6 +108,7 @@ private slots:
void subscribeGroup(bool subscribe); void subscribeGroup(bool subscribe);
void createthread(); void createthread();
void togglePinUpPost();
void createmessage(); void createmessage();
void previousMessage(); void previousMessage();

View file

@ -182,7 +182,7 @@
<widget class="QComboBox" name="viewBox"> <widget class="QComboBox" name="viewBox">
<item> <item>
<property name="text"> <property name="text">
<string>Last Post</string> <string>Lastest post in thread</string>
</property> </property>
</item> </item>
<item> <item>
@ -577,8 +577,8 @@
</customwidget> </customwidget>
</customwidgets> </customwidgets>
<resources> <resources>
<include location="../images.qrc"/>
<include location="../icons.qrc"/> <include location="../icons.qrc"/>
<include location="../images.qrc"/>
</resources> </resources>
<connections/> <connections/>
</ui> </ui>

View file

@ -94,57 +94,101 @@ static bool decreasing_time_comp(const QPair<time_t,RsGxsMessageId>& e1,const QP
void GxsForumsFillThread::run() void GxsForumsFillThread::run()
{ {
RsTokenService *service = rsGxsForums->getTokenService(); RsTokenService *service = rsGxsForums->getTokenService();
uint32_t msg_token;
uint32_t grp_token;
emit status(tr("Waiting")); emit status(tr("Waiting"));
/* get all messages of the forum */ {
RsTokReqOptions opts; /* get all messages of the forum */
opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA; RsTokReqOptions opts;
opts.mReqType = GXS_REQUEST_TYPE_MSG_DATA;
std::list<RsGxsGroupId> grpIds; std::list<RsGxsGroupId> grpIds;
grpIds.push_back(mForumId); grpIds.push_back(mForumId);
#ifdef DEBUG_FORUMS #ifdef DEBUG_FORUMS
std::cerr << "GxsForumsFillThread::run() forum id " << mForumId << std::endl; std::cerr << "GxsForumsFillThread::run() forum id " << mForumId << std::endl;
#endif #endif
uint32_t token; service->requestMsgInfo(msg_token, RS_TOKREQ_ANSTYPE_DATA, opts, grpIds);
service->requestMsgInfo(token, RS_TOKREQ_ANSTYPE_DATA, opts, grpIds);
/* wait for the answer */ /* wait for the answer */
uint32_t requestStatus = RsTokenService::PENDING; uint32_t requestStatus = RsTokenService::PENDING;
while (!wasStopped()) { while (!wasStopped()) {
requestStatus = service->requestStatus(token); requestStatus = service->requestStatus(msg_token);
if (requestStatus == RsTokenService::FAILED || if (requestStatus == RsTokenService::FAILED ||
requestStatus == RsTokenService::COMPLETE) { requestStatus == RsTokenService::COMPLETE) {
break; break;
}
msleep(200);
} }
msleep(100);
if (requestStatus == RsTokenService::FAILED)
return;
} }
if (wasStopped()) { // also get the forum meta data.
{
RsTokReqOptions opts;
opts.mReqType = GXS_REQUEST_TYPE_GROUP_DATA;
std::list<RsGxsGroupId> grpIds;
grpIds.push_back(mForumId);
#ifdef DEBUG_FORUMS
std::cerr << "GxsForumsFillThread::run() forum id " << mForumId << std::endl;
#endif
service->requestGroupInfo(grp_token, RS_TOKREQ_ANSTYPE_DATA, opts, grpIds);
/* wait for the answer */
uint32_t requestStatus = RsTokenService::PENDING;
while (!wasStopped()) {
requestStatus = service->requestStatus(grp_token);
if (requestStatus == RsTokenService::FAILED ||
requestStatus == RsTokenService::COMPLETE) {
break;
}
msleep(200);
}
if (requestStatus == RsTokenService::FAILED)
return;
}
if (wasStopped())
{
#ifdef DEBUG_FORUMS #ifdef DEBUG_FORUMS
std::cerr << "GxsForumsFillThread::run() thread stopped, cancel request" << std::endl; std::cerr << "GxsForumsFillThread::run() thread stopped, cancel request" << std::endl;
#endif #endif
/* cancel request */ /* cancel request */
service->cancelRequest(token); service->cancelRequest(msg_token);
service->cancelRequest(grp_token);
return; return;
} }
if (requestStatus == RsTokenService::FAILED) {
//#TODO
return;
}
//#TODO
// if (failed) {
// mService->cancelRequest(token);
// return;
// }
emit status(tr("Retrieving")); emit status(tr("Retrieving"));
std::vector<RsGxsForumGroup> forum_groups;
if (!rsGxsForums->getGroupData(grp_token, forum_groups) || forum_groups.size() != 1)
return;
RsGxsForumGroup forum_group = *forum_groups.begin();
//#ifdef DEBUG_FORUMS
std::cerr << "Retrieved group data: " << std::endl;
std::cerr << " Group ID: " << forum_group.mMeta.mGroupId << std::endl;
std::cerr << " Admin lst: " << forum_group.mAdminList.ids.size() << " elements." << std::endl;
for(auto it(forum_group.mAdminList.ids.begin());it!=forum_group.mAdminList.ids.end();++it)
std::cerr << " " << *it << std::endl;
std::cerr << " Pinned Post: " << forum_group.mPinnedPosts.ids.size() << " messages." << std::endl;
for(auto it(forum_group.mPinnedPosts.ids.begin());it!=forum_group.mPinnedPosts.ids.end();++it)
std::cerr << " " << *it << std::endl;
//#endif
/* get messages */ /* get messages */
std::map<RsGxsMessageId,RsGxsForumMsg> msgs; std::map<RsGxsMessageId,RsGxsForumMsg> msgs;
@ -152,9 +196,8 @@ void GxsForumsFillThread::run()
std::vector<RsGxsForumMsg> msgs_array; std::vector<RsGxsForumMsg> msgs_array;
if (!rsGxsForums->getMsgData(token, msgs_array)) { if (!rsGxsForums->getMsgData(msg_token, msgs_array))
return; return;
}
// now put everything into a map in order to make search log(n) // now put everything into a map in order to make search log(n)
@ -209,11 +252,17 @@ void GxsForumsFillThread::run()
if(msgIt2 == msgs.end()) if(msgIt2 == msgs.end())
continue ; continue ;
// Make sure that the author is the same than the original message. This should always happen, but nothing can prevent someone to // Make sure that the author is the same than the original message, or is a moderator. This should always happen when messages are constructed using
// craft a new version of a message with his own signature. // the UI but nothing can prevent a nasty user to craft a new version of a message with his own signature.
if(msgIt2->second.mMeta.mAuthorId != msgIt->second.mMeta.mAuthorId) if(msgIt2->second.mMeta.mAuthorId != msgIt->second.mMeta.mAuthorId)
continue ; {
if( !IS_FORUM_MSG_MODERATION(msgIt->second.mMeta.mMsgFlags) ) // if authors are different the moderation flag needs to be set on the editing msg
continue ;
if( forum_group.mAdminList.ids.find(msgIt->second.mMeta.mAuthorId)==forum_group.mAdminList.ids.end()) // if author is not a moderator, continue
continue ;
}
// always add the post a self version // always add the post a self version