Refactor v12 support to use new user_level helper from mtxclient
This commit is contained in:
parent
3184ab464c
commit
e3bc058845
18 changed files with 208 additions and 207 deletions
|
|
@ -622,7 +622,7 @@ if(USE_BUNDLED_MTXCLIENT)
|
|||
FetchContent_Declare(
|
||||
MatrixClient
|
||||
GIT_REPOSITORY https://github.com/Nheko-Reborn/mtxclient.git
|
||||
GIT_TAG d6f10427d1c5e5b1a45f426274f8d2e8dd0b64be
|
||||
GIT_TAG 873911e352a0845dfb178f77b1ddea796a5d3455
|
||||
)
|
||||
set(BUILD_LIB_EXAMPLES OFF CACHE INTERNAL "")
|
||||
set(BUILD_LIB_TESTS OFF CACHE INTERNAL "")
|
||||
|
|
|
|||
|
|
@ -213,8 +213,8 @@ modules:
|
|||
- -DBUILD_SHARED_LIBS=OFF
|
||||
buildsystem: cmake-ninja
|
||||
sources:
|
||||
- commit: 15b43844f4ec27faa5f2ec92c4ded313206763aa
|
||||
tag: v0.10.1
|
||||
- commit: 873911e352a0845dfb178f77b1ddea796a5d3455
|
||||
#tag: v0.10.1
|
||||
type: git
|
||||
url: https://github.com/Nheko-Reborn/mtxclient.git
|
||||
- name: nheko
|
||||
|
|
|
|||
|
|
@ -89,7 +89,6 @@ Column {
|
|||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
isV12Creator: room.isV12Creator(userId)
|
||||
powerlevel: userPowerlevel
|
||||
height: fontMetrics.ascent
|
||||
width: height
|
||||
|
|
@ -98,7 +97,7 @@ Column {
|
|||
sourceSize.height: height
|
||||
|
||||
permissions: room ? room.permissions : null
|
||||
visible: isAdmin || isModerator || isV12Creator
|
||||
visible: isAdmin || isModerator // implicitly includes creators as well
|
||||
}
|
||||
|
||||
ToolTip.delay: Nheko.tooltipDelay
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ import QtQuick.Controls
|
|||
import im.nheko
|
||||
|
||||
Image {
|
||||
required property int powerlevel
|
||||
required property var powerlevel
|
||||
required property var permissions
|
||||
required property bool isV12Creator
|
||||
|
||||
readonly property bool isV12Creator: permissions ? permissions.creatorLevel() == powerlevel : false
|
||||
readonly property bool isAdmin: permissions ? permissions.changeLevel(MtxEvent.PowerLevels) <= powerlevel : false
|
||||
readonly property bool isModerator: permissions ? permissions.redactLevel() <= powerlevel : false
|
||||
readonly property bool isDefault: permissions ? permissions.defaultLevel() <= powerlevel : false
|
||||
|
|
@ -27,14 +27,15 @@ Image {
|
|||
source: sourceUrl + (ma.hovered ? palette.highlight : palette.buttonText)
|
||||
ToolTip.visible: ma.hovered
|
||||
ToolTip.text: {
|
||||
let pl = powerlevel.toLocaleString(Qt.locale(), "f", 0);
|
||||
if (isV12Creator)
|
||||
return qsTr("Creator");
|
||||
else if (isAdmin)
|
||||
return qsTr("Administrator: %1").arg(powerlevel);
|
||||
return qsTr("Administrator (%1)").arg(pl)
|
||||
else if (isModerator)
|
||||
return qsTr("Moderator: %1").arg(powerlevel);
|
||||
return qsTr("Moderator: %1").arg(pl);
|
||||
else
|
||||
return qsTr("User: %1").arg(powerlevel);
|
||||
return qsTr("User: %1").arg(pl);
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
|
|
|
|||
|
|
@ -94,14 +94,17 @@ ApplicationWindow {
|
|||
Text {
|
||||
visible: !model.isType;
|
||||
text: {
|
||||
let pl = model.powerlevel.toLocaleString(Qt.locale(), "f", 0);
|
||||
if (editingModel.creatorLevel == model.powerlevel)
|
||||
return qsTr("Creator")
|
||||
if (editingModel.adminLevel == model.powerlevel)
|
||||
return qsTr("Administrator (%1)").arg(model.powerlevel)
|
||||
return qsTr("Administrator (%1)").arg(pl)
|
||||
else if (editingModel.moderatorLevel == model.powerlevel)
|
||||
return qsTr("Moderator (%1)").arg(model.powerlevel)
|
||||
return qsTr("Moderator (%1)").arg(pl)
|
||||
else if (editingModel.defaultUserLevel == model.powerlevel)
|
||||
return qsTr("User (%1)").arg(model.powerlevel)
|
||||
return qsTr("User (%1)").arg(pl)
|
||||
else
|
||||
return qsTr("Custom (%1)").arg(model.powerlevel)
|
||||
return qsTr("Custom (%1)").arg(pl)
|
||||
}
|
||||
color: palette.text
|
||||
}
|
||||
|
|
@ -138,7 +141,7 @@ ApplicationWindow {
|
|||
|
||||
color: palette.text
|
||||
|
||||
Keys.onPressed: {
|
||||
Keys.onPressed: event => {
|
||||
if (typeEntry.text.includes('.') && event.matches(StandardKey.InsertParagraphSeparator)) {
|
||||
editingModel.types.add(typeEntry.index, typeEntry.text)
|
||||
typeEntry.visible = false;
|
||||
|
|
@ -334,12 +337,17 @@ ApplicationWindow {
|
|||
Text {
|
||||
visible: !model.isUser;
|
||||
text: {
|
||||
let pl = model.powerlevel.toLocaleString(Qt.locale(), "f", 0);
|
||||
if (editingModel.creatorLevel == model.powerlevel)
|
||||
return qsTr("Creator")
|
||||
if (editingModel.adminLevel == model.powerlevel)
|
||||
return qsTr("Administrator (%1)").arg(model.powerlevel)
|
||||
return qsTr("Administrator (%1)").arg(pl)
|
||||
else if (editingModel.moderatorLevel == model.powerlevel)
|
||||
return qsTr("Moderator (%1)").arg(model.powerlevel)
|
||||
return qsTr("Moderator (%1)").arg(pl)
|
||||
else if (editingModel.defaultUserLevel == model.powerlevel)
|
||||
return qsTr("User (%1)").arg(pl)
|
||||
else
|
||||
return qsTr("Custom (%1)").arg(model.powerlevel)
|
||||
return qsTr("Custom (%1)").arg(pl)
|
||||
}
|
||||
color: palette.text
|
||||
}
|
||||
|
|
@ -349,7 +357,7 @@ ApplicationWindow {
|
|||
Layout.alignment: Qt.AlignRight
|
||||
Layout.rightMargin: 2
|
||||
image: model.isUser ? ":/icons/icons/ui/dismiss.svg" : ":/icons/icons/ui/add-square-button.svg"
|
||||
visible: !model.isUser || model.removeable
|
||||
visible: (!model.isUser || model.removeable) && model.powerlevel != editingModel.creatorLevel
|
||||
hoverEnabled: true
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: model.isUser ? qsTr("Remove user") : qsTr("Add user")
|
||||
|
|
|
|||
|
|
@ -168,7 +168,6 @@ ApplicationWindow {
|
|||
sourceSize.height: height
|
||||
powerlevel: model.powerlevel
|
||||
permissions: room.permissions
|
||||
isV12Creator: room.isV12Creator(model.mxid)
|
||||
}
|
||||
|
||||
EncryptionIndicator {
|
||||
|
|
|
|||
|
|
@ -4621,12 +4621,14 @@ Cache::updateSpaces(lmdb::txn &txn,
|
|||
event.state_key.at(0) == '!') {
|
||||
const std::string &space = event.state_key;
|
||||
|
||||
auto create = getStateEvent<mtx::events::state::Create>(txn, space)
|
||||
.value_or(mtx::events::StateEvent<mtx::events::state::Create>{});
|
||||
auto pls = getStateEvent<mtx::events::state::PowerLevels>(txn, space);
|
||||
|
||||
if (!pls)
|
||||
continue;
|
||||
|
||||
if (pls->content.user_level(event.sender) >=
|
||||
if (pls->content.user_level(event.sender, create) >=
|
||||
pls->content.state_level(space_event_type)) {
|
||||
db->spacesChildren.put(txn, space, room);
|
||||
db->spacesParents.put(txn, room, space);
|
||||
|
|
@ -4635,7 +4637,7 @@ Cache::updateSpaces(lmdb::txn &txn,
|
|||
room,
|
||||
space,
|
||||
event.sender,
|
||||
pls->content.user_level(event.sender),
|
||||
pls->content.user_level(event.sender, create),
|
||||
pls->content.state_level(space_event_type));
|
||||
}
|
||||
}
|
||||
|
|
@ -4851,51 +4853,6 @@ Cache::getAccountData(lmdb::txn &txn, mtx::events::EventType type, const std::st
|
|||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool
|
||||
Cache::isV12Creator(const std::string &room_id, const std::string &user_id)
|
||||
{
|
||||
auto txn = ro_txn(this->db->env_);
|
||||
auto state = this->getStatesDb(txn, room_id);
|
||||
|
||||
return this->isV12Creator(txn, state, user_id);
|
||||
}
|
||||
|
||||
bool
|
||||
Cache::isV12Creator(lmdb::txn &txn, lmdb::dbi &state, const std::string &user_id)
|
||||
{
|
||||
using namespace mtx::events;
|
||||
using namespace mtx::events::state;
|
||||
|
||||
bool ok;
|
||||
const int room_version = this->getRoomVersion(txn, state).toInt(&ok);
|
||||
if (!ok || room_version < 12) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string_view create_event;
|
||||
if (state.get(txn, to_string(EventType::RoomCreate), create_event)) {
|
||||
try {
|
||||
const StateEvent<Create> evt =
|
||||
nlohmann::json::parse(create_event).get<StateEvent<Create>>();
|
||||
if (evt.sender == user_id) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const std::optional<std::vector<std::string>> &additional_creators =
|
||||
evt.content.additional_creators;
|
||||
if (additional_creators &&
|
||||
std::find(additional_creators->begin(), additional_creators->end(), user_id) !=
|
||||
additional_creators->end()) {
|
||||
return true;
|
||||
}
|
||||
} catch (...) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
Cache::hasEnoughPowerLevel(const std::vector<mtx::events::EventType> &eventTypes,
|
||||
const std::string &room_id,
|
||||
|
|
@ -4908,30 +4865,22 @@ Cache::hasEnoughPowerLevel(const std::vector<mtx::events::EventType> &eventTypes
|
|||
try {
|
||||
auto db_ = getStatesDb(txn, room_id);
|
||||
|
||||
if (this->isV12Creator(txn, db_, user_id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
int64_t min_event_level = std::numeric_limits<int64_t>::max();
|
||||
int64_t user_level = std::numeric_limits<int64_t>::min();
|
||||
|
||||
std::string_view event;
|
||||
bool res = db_.get(txn, to_string(EventType::RoomPowerLevels), event);
|
||||
try {
|
||||
StateEvent<Create> create = getStateEvent<mtx::events::state::Create>(txn, room_id)
|
||||
.value_or(StateEvent<Create>{});
|
||||
StateEvent<PowerLevels> pls =
|
||||
getStateEvent<mtx::events::state::PowerLevels>(txn, room_id)
|
||||
.value_or(StateEvent<PowerLevels>{});
|
||||
|
||||
if (res) {
|
||||
try {
|
||||
StateEvent<PowerLevels> msg =
|
||||
nlohmann::json::parse(std::string_view(event.data(), event.size()))
|
||||
.get<StateEvent<PowerLevels>>();
|
||||
user_level = pls.content.user_level(user_id, create);
|
||||
|
||||
user_level = msg.content.user_level(user_id);
|
||||
|
||||
for (const auto &ty : eventTypes)
|
||||
min_event_level =
|
||||
std::min(min_event_level, msg.content.state_level(to_string(ty)));
|
||||
} catch (const nlohmann::json::exception &e) {
|
||||
nhlog::db()->warn("failed to parse m.room.power_levels event: {}", e.what());
|
||||
}
|
||||
for (const auto &ty : eventTypes)
|
||||
min_event_level = std::min(min_event_level, pls.content.state_level(to_string(ty)));
|
||||
} catch (const nlohmann::json::exception &e) {
|
||||
nhlog::db()->warn("failed to parse m.room.power_levels event: {}", e.what());
|
||||
}
|
||||
|
||||
return user_level >= min_event_level;
|
||||
|
|
@ -6178,13 +6127,6 @@ roomMembers(const std::string &room_id)
|
|||
return instance_->roomMembers(room_id);
|
||||
}
|
||||
|
||||
//! Check if the given user is a room creator and that gives them an infinite PL.
|
||||
bool
|
||||
isV12Creator(const std::string &room_id, const std::string &user_id)
|
||||
{
|
||||
return instance_->isV12Creator(room_id, user_id);
|
||||
}
|
||||
|
||||
//! Check if the given user has power level greater than
|
||||
//! lowest power level of the given events.
|
||||
bool
|
||||
|
|
@ -6466,6 +6408,7 @@ NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::JoinRules)
|
|||
NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::Name)
|
||||
NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::PinnedEvents)
|
||||
NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::PowerLevels)
|
||||
NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::Create)
|
||||
NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::ServerAcl)
|
||||
NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::space::Child)
|
||||
NHEKO_CACHE_GET_STATE_EVENT_DEFINITION(mtx::events::state::space::Parent)
|
||||
|
|
|
|||
|
|
@ -123,10 +123,6 @@ runMigrations();
|
|||
std::vector<std::string>
|
||||
roomMembers(const std::string &room_id);
|
||||
|
||||
//! Check if the given user is a room creator and that gives them an infinite PL.
|
||||
bool
|
||||
isV12Creator(const std::string &room_id, const std::string &user_id);
|
||||
|
||||
//! Check if the given user has power level greater than than
|
||||
//! lowest power level of the given events.
|
||||
bool
|
||||
|
|
|
|||
|
|
@ -145,10 +145,6 @@ public:
|
|||
//! Retrieve all the user ids from a room.
|
||||
std::vector<std::string> roomMembers(const std::string &room_id);
|
||||
|
||||
//! Check if the given user is a room creator and that gives them an infinite PL.
|
||||
bool isV12Creator(const std::string &room_id, const std::string &user_id);
|
||||
bool isV12Creator(lmdb::txn &txn, lmdb::dbi &state, const std::string &user_id);
|
||||
|
||||
//! Check if the given user has power leve greater than than
|
||||
//! lowest power level of the given events.
|
||||
bool hasEnoughPowerLevel(const std::vector<mtx::events::EventType> &eventTypes,
|
||||
|
|
|
|||
|
|
@ -19,6 +19,9 @@ MemberListBackend::MemberListBackend(const QString &room_id, QObject *parent)
|
|||
->getStateEvent<mtx::events::state::PowerLevels>(room_id_.toStdString())
|
||||
.value_or(mtx::events::StateEvent<mtx::events::state::PowerLevels>{})
|
||||
.content}
|
||||
, create_{cache::client()
|
||||
->getStateEvent<mtx::events::state::Create>(room_id_.toStdString())
|
||||
.value_or(mtx::events::StateEvent<mtx::events::state::Create>{})}
|
||||
{
|
||||
try {
|
||||
info_ = cache::singleRoomInfo(room_id_.toStdString());
|
||||
|
|
@ -92,7 +95,7 @@ MemberListBackend::data(const QModelIndex &index, int role) const
|
|||
}
|
||||
case Powerlevel:
|
||||
return static_cast<qlonglong>(
|
||||
powerLevels_.user_level(m_memberList[index.row()].first.user_id.toStdString()));
|
||||
powerLevels_.user_level(m_memberList[index.row()].first.user_id.toStdString(), create_));
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
|
|
@ -172,28 +175,4 @@ MemberList::filterAcceptsRow(int source_row, const QModelIndex &) const
|
|||
Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
bool
|
||||
MemberList::lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const
|
||||
{
|
||||
if (this->sortRole() != MemberSortRoles::Powerlevel) {
|
||||
return QSortFilterProxyModel::lessThan(source_left, source_right);
|
||||
}
|
||||
|
||||
const QString &left =
|
||||
this->m_model.data(source_left, MemberListBackend::Roles::Mxid).toString();
|
||||
const QString &right =
|
||||
this->m_model.data(source_right, MemberListBackend::Roles::Mxid).toString();
|
||||
|
||||
const std::string &room_id = this->roomId().toStdString();
|
||||
if (cache::isV12Creator(room_id, left.toStdString())) {
|
||||
if (!cache::isV12Creator(room_id, right.toStdString())) {
|
||||
return false;
|
||||
}
|
||||
// If both are creators, sort by mxid.
|
||||
return left < right;
|
||||
}
|
||||
|
||||
return QSortFilterProxyModel::lessThan(source_left, source_right);
|
||||
}
|
||||
|
||||
#include "moc_MemberList.cpp"
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ private:
|
|||
bool loadingMoreMembers_{false};
|
||||
|
||||
mtx::events::state::PowerLevels powerLevels_;
|
||||
mtx::events::StateEvent<mtx::events::state::Create> create_;
|
||||
|
||||
friend class MemberList;
|
||||
};
|
||||
|
|
@ -122,7 +123,6 @@ public slots:
|
|||
|
||||
protected:
|
||||
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
|
||||
bool lessThan(const QModelIndex &source_left, const QModelIndex &source_right) const override;
|
||||
|
||||
private:
|
||||
QString filterString;
|
||||
|
|
|
|||
|
|
@ -17,12 +17,15 @@
|
|||
#include "Logging.h"
|
||||
#include "MatrixClient.h"
|
||||
|
||||
PowerlevelsTypeListModel::PowerlevelsTypeListModel(const std::string &rid,
|
||||
const mtx::events::state::PowerLevels &pl,
|
||||
QObject *parent)
|
||||
PowerlevelsTypeListModel::PowerlevelsTypeListModel(
|
||||
const std::string &rid,
|
||||
const mtx::events::state::PowerLevels &pl,
|
||||
const mtx::events::StateEvent<mtx::events::state::Create> &create,
|
||||
QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
, room_id(rid)
|
||||
, powerLevels_(pl)
|
||||
, create_(create)
|
||||
{
|
||||
std::set<mtx::events::state::power_level_t> seen_levels;
|
||||
for (const auto &[type, level] : powerLevels_.events) {
|
||||
|
|
@ -40,6 +43,9 @@ PowerlevelsTypeListModel::PowerlevelsTypeListModel(const std::string &rid,
|
|||
seen_levels.insert(level);
|
||||
}
|
||||
}
|
||||
if (create_.content.room_version_creators_with_infinite_power()) {
|
||||
seen_levels.insert(mtx::events::state::Creator);
|
||||
}
|
||||
|
||||
for (const auto &level : {
|
||||
powerLevels_.events_default,
|
||||
|
|
@ -354,12 +360,15 @@ PowerlevelsTypeListModel::moveRows(const QModelIndex &,
|
|||
return true;
|
||||
}
|
||||
|
||||
PowerlevelsUserListModel::PowerlevelsUserListModel(const std::string &rid,
|
||||
const mtx::events::state::PowerLevels &pl,
|
||||
QObject *parent)
|
||||
PowerlevelsUserListModel::PowerlevelsUserListModel(
|
||||
const std::string &rid,
|
||||
const mtx::events::state::PowerLevels &pl,
|
||||
const mtx::events::StateEvent<mtx::events::state::Create> &create,
|
||||
QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
, room_id(rid)
|
||||
, powerLevels_(pl)
|
||||
, create_(create)
|
||||
{
|
||||
std::set<mtx::events::state::power_level_t> seen_levels;
|
||||
for (const auto &[user, level] : powerLevels_.users) {
|
||||
|
|
@ -378,6 +387,16 @@ PowerlevelsUserListModel::PowerlevelsUserListModel(const std::string &rid,
|
|||
}
|
||||
}
|
||||
|
||||
if (create_.content.room_version_creators_with_infinite_power()) {
|
||||
users.push_back(Entry{"", mtx::events::state::Creator});
|
||||
seen_levels.insert(mtx::events::state::Creator);
|
||||
|
||||
users.push_back(Entry{create_.sender, mtx::events::state::Creator});
|
||||
for (const auto &user : create.content.additional_creators) {
|
||||
users.push_back(Entry{user, mtx::events::state::Creator});
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &level : {
|
||||
powerLevels_.events_default,
|
||||
powerLevels_.state_default,
|
||||
|
|
@ -408,7 +427,7 @@ PowerlevelsUserListModel::toUsers() const
|
|||
{
|
||||
std::map<std::string, mtx::events::state::power_level_t, std::less<>> m;
|
||||
for (const auto &[key, pl] : std::as_const(users))
|
||||
if (key.size() > 0 && key.at(0) == '@')
|
||||
if (key.size() > 0 && key.at(0) == '@' && pl != mtx::events::state::Creator)
|
||||
m[key] = pl;
|
||||
return m;
|
||||
}
|
||||
|
|
@ -459,7 +478,7 @@ PowerlevelsUserListModel::data(const QModelIndex &index, int role) const
|
|||
case IsUser:
|
||||
return !user.mxid.empty();
|
||||
case Moveable:
|
||||
return !user.mxid.empty();
|
||||
return !user.mxid.empty() && user.pl != mtx::events::state::Creator;
|
||||
case Removeable:
|
||||
return !user.mxid.empty() && user.mxid.find('.') != std::string::npos;
|
||||
}
|
||||
|
|
@ -554,7 +573,15 @@ PowerlevelsUserListModel::moveRows(const QModelIndex &,
|
|||
if (users.at(sourceRow).mxid.empty())
|
||||
return false;
|
||||
|
||||
auto pl = users.at(destinationChild > 0 ? destinationChild - 1 : 0).pl;
|
||||
if (users.at(sourceRow).pl == mtx::events::state::Creator)
|
||||
return false;
|
||||
if (users.at(destinationChild).pl == mtx::events::state::Creator)
|
||||
return false;
|
||||
|
||||
auto pl = users.at(destinationChild > 0 ? destinationChild - 1 : 0).pl;
|
||||
if (pl == mtx::events::state::Creator)
|
||||
return false;
|
||||
|
||||
auto sourceItem = users.takeAt(sourceRow);
|
||||
sourceItem.pl = pl;
|
||||
|
||||
|
|
@ -577,9 +604,12 @@ PowerlevelEditingModels::PowerlevelEditingModels(QString room_id, QObject *paren
|
|||
->getStateEvent<mtx::events::state::PowerLevels>(room_id.toStdString())
|
||||
.value_or(mtx::events::StateEvent<mtx::events::state::PowerLevels>{})
|
||||
.content)
|
||||
, types_(room_id.toStdString(), powerLevels_, this)
|
||||
, users_(room_id.toStdString(), powerLevels_, this)
|
||||
, spaces_(room_id.toStdString(), powerLevels_, this)
|
||||
, create_(cache::client()
|
||||
->getStateEvent<mtx::events::state::Create>(room_id.toStdString())
|
||||
.value_or(mtx::events::StateEvent<mtx::events::state::Create>{}))
|
||||
, types_(room_id.toStdString(), powerLevels_, create_, this)
|
||||
, users_(room_id.toStdString(), powerLevels_, create_, this)
|
||||
, spaces_(room_id.toStdString(), powerLevels_, create_, this)
|
||||
, room_id_(room_id.toStdString())
|
||||
{
|
||||
connect(&types_,
|
||||
|
|
@ -678,16 +708,18 @@ samePl(const mtx::events::state::PowerLevels &a, const mtx::events::state::Power
|
|||
b.redact);
|
||||
}
|
||||
|
||||
PowerlevelsSpacesListModel::PowerlevelsSpacesListModel(const std::string &room_id_,
|
||||
const mtx::events::state::PowerLevels &pl,
|
||||
QObject *parent)
|
||||
PowerlevelsSpacesListModel::PowerlevelsSpacesListModel(
|
||||
const std::string &room_id_,
|
||||
const mtx::events::state::PowerLevels &pl,
|
||||
const mtx::events::StateEvent<mtx::events::state::Create> &create,
|
||||
QObject *parent)
|
||||
: QAbstractListModel(parent)
|
||||
, room_id(std::move(room_id_))
|
||||
, oldPowerLevels_(std::move(pl))
|
||||
{
|
||||
beginResetModel();
|
||||
|
||||
spaces.push_back(Entry{room_id, oldPowerLevels_, true});
|
||||
spaces.push_back(Entry{room_id, oldPowerLevels_, create, true});
|
||||
|
||||
std::unordered_set<std::string> visited;
|
||||
|
||||
|
|
@ -703,10 +735,16 @@ PowerlevelsSpacesListModel::PowerlevelsSpacesListModel(const std::string &room_i
|
|||
cache::client()->getStateEvent<mtx::events::state::space::Parent>(s, space);
|
||||
if (parent && parent->content.via && !parent->content.via->empty() &&
|
||||
parent->content.canonical) {
|
||||
auto parentPl = cache::client()->getStateEvent<mtx::events::state::PowerLevels>(s);
|
||||
auto childPl = cache::client()->getStateEvent<mtx::events::state::PowerLevels>(s);
|
||||
auto childCreate =
|
||||
cache::client()->getStateEvent<mtx::events::state::Create>(s).value_or(
|
||||
mtx::events::StateEvent<mtx::events::state::Create>{});
|
||||
|
||||
spaces.push_back(Entry{
|
||||
s, parentPl ? parentPl->content : mtx::events::state::PowerLevels{}, false});
|
||||
spaces.push_back(
|
||||
Entry{s,
|
||||
childPl ? childPl->content : mtx::events::state::PowerLevels{},
|
||||
childCreate,
|
||||
false});
|
||||
addChildren(s);
|
||||
}
|
||||
}
|
||||
|
|
@ -813,7 +851,7 @@ PowerlevelsSpacesListModel::data(QModelIndex const &index, int role) const
|
|||
auto entry = spaces.at(row);
|
||||
switch (role) {
|
||||
case Roles::IsEditable:
|
||||
return entry.pl.user_level(http::client()->user_id().to_string()) >=
|
||||
return entry.pl.user_level(http::client()->user_id().to_string(), entry.create) >=
|
||||
entry.pl.state_level(to_string(mtx::events::EventType::RoomPowerLevels));
|
||||
case Roles::IsDifferentFromBase:
|
||||
return !samePl(entry.pl, oldPowerLevels_);
|
||||
|
|
|
|||
|
|
@ -29,9 +29,11 @@ public:
|
|||
Removeable,
|
||||
};
|
||||
|
||||
explicit PowerlevelsTypeListModel(const std::string &room_id_,
|
||||
const mtx::events::state::PowerLevels &pl,
|
||||
QObject *parent = nullptr);
|
||||
explicit PowerlevelsTypeListModel(
|
||||
const std::string &room_id_,
|
||||
const mtx::events::state::PowerLevels &pl,
|
||||
const mtx::events::StateEvent<mtx::events::state::Create> &create,
|
||||
QObject *parent = nullptr);
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
int rowCount(const QModelIndex &) const override { return static_cast<int>(types.size()); }
|
||||
|
|
@ -67,6 +69,7 @@ public:
|
|||
std::string room_id;
|
||||
QVector<Entry> types;
|
||||
mtx::events::state::PowerLevels powerLevels_;
|
||||
mtx::events::StateEvent<mtx::events::state::Create> create_;
|
||||
};
|
||||
|
||||
class PowerlevelsUserListModel final : public QAbstractListModel
|
||||
|
|
@ -88,9 +91,11 @@ public:
|
|||
Removeable,
|
||||
};
|
||||
|
||||
explicit PowerlevelsUserListModel(const std::string &room_id_,
|
||||
const mtx::events::state::PowerLevels &pl,
|
||||
QObject *parent = nullptr);
|
||||
explicit PowerlevelsUserListModel(
|
||||
const std::string &room_id_,
|
||||
const mtx::events::state::PowerLevels &pl,
|
||||
const mtx::events::StateEvent<mtx::events::state::Create> &create,
|
||||
QObject *parent = nullptr);
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
int rowCount(const QModelIndex &) const override { return static_cast<int>(users.size()); }
|
||||
|
|
@ -121,6 +126,7 @@ public:
|
|||
std::string room_id;
|
||||
QVector<Entry> users;
|
||||
mtx::events::state::PowerLevels powerLevels_;
|
||||
mtx::events::StateEvent<mtx::events::state::Create> create_;
|
||||
};
|
||||
|
||||
class PowerlevelsSpacesListModel final : public QAbstractListModel
|
||||
|
|
@ -147,9 +153,11 @@ public:
|
|||
ApplyPermissions,
|
||||
};
|
||||
|
||||
explicit PowerlevelsSpacesListModel(const std::string &room_id_,
|
||||
const mtx::events::state::PowerLevels &pl,
|
||||
QObject *parent = nullptr);
|
||||
explicit PowerlevelsSpacesListModel(
|
||||
const std::string &room_id_,
|
||||
const mtx::events::state::PowerLevels &pl,
|
||||
const mtx::events::StateEvent<mtx::events::state::Create> &create,
|
||||
QObject *parent = nullptr);
|
||||
|
||||
QHash<int, QByteArray> roleNames() const override;
|
||||
int rowCount(const QModelIndex &) const override { return static_cast<int>(spaces.size()); }
|
||||
|
|
@ -183,6 +191,7 @@ public:
|
|||
|
||||
std::string roomid;
|
||||
mtx::events::state::PowerLevels pl;
|
||||
mtx::events::StateEvent<mtx::events::state::Create> create;
|
||||
bool apply = false;
|
||||
};
|
||||
|
||||
|
|
@ -203,6 +212,7 @@ class PowerlevelEditingModels final : public QObject
|
|||
Q_PROPERTY(PowerlevelsUserListModel *users READ users CONSTANT)
|
||||
Q_PROPERTY(PowerlevelsTypeListModel *types READ types CONSTANT)
|
||||
Q_PROPERTY(PowerlevelsSpacesListModel *spaces READ spaces CONSTANT)
|
||||
Q_PROPERTY(qlonglong creatorLevel READ creatorLevel CONSTANT)
|
||||
Q_PROPERTY(qlonglong adminLevel READ adminLevel NOTIFY adminLevelChanged)
|
||||
Q_PROPERTY(qlonglong moderatorLevel READ moderatorLevel NOTIFY moderatorLevelChanged)
|
||||
Q_PROPERTY(qlonglong defaultUserLevel READ defaultUserLevel NOTIFY defaultUserLevelChanged)
|
||||
|
|
@ -222,6 +232,7 @@ public:
|
|||
PowerlevelsUserListModel *users() { return &users_; }
|
||||
PowerlevelsTypeListModel *types() { return &types_; }
|
||||
PowerlevelsSpacesListModel *spaces() { return &spaces_; }
|
||||
qlonglong creatorLevel() const { return mtx::events::state::Creator; }
|
||||
qlonglong adminLevel() const
|
||||
{
|
||||
return powerLevels_.state_level(to_string(mtx::events::EventType::RoomPowerLevels));
|
||||
|
|
@ -235,6 +246,7 @@ public:
|
|||
Q_INVOKABLE void addRole(int pl);
|
||||
|
||||
mtx::events::state::PowerLevels powerLevels_;
|
||||
mtx::events::StateEvent<mtx::events::state::Create> create_;
|
||||
PowerlevelsTypeListModel types_;
|
||||
PowerlevelsUserListModel users_;
|
||||
PowerlevelsSpacesListModel spaces_;
|
||||
|
|
|
|||
|
|
@ -1453,6 +1453,9 @@ utils::roomVias(const std::string &roomid)
|
|||
auto powerlevels =
|
||||
cache::client()->getStateEvent<mtx::events::state::PowerLevels>(roomid).value_or(
|
||||
mtx::events::StateEvent<mtx::events::state::PowerLevels>{});
|
||||
auto create =
|
||||
cache::client()->getStateEvent<mtx::events::state::Create>(roomid).value_or(
|
||||
mtx::events::StateEvent<mtx::events::state::Create>{});
|
||||
auto acls = cache::client()->getStateEvent<mtx::events::state::ServerAcl>(roomid);
|
||||
|
||||
std::vector<QRegularExpression> allowedServers;
|
||||
|
|
@ -1501,6 +1504,19 @@ utils::roomVias(const std::string &roomid)
|
|||
std::set<std::string> users_with_high_pl_in_room;
|
||||
// we should pick PL > 50, but imo that is broken, so we just pick users who have admins
|
||||
// perm
|
||||
if (create.content.room_version_creators_with_infinite_power()) {
|
||||
{
|
||||
auto user = create.sender;
|
||||
auto host = mtx::identifiers::parse<mtx::identifiers::User>(user).hostname();
|
||||
if (isHostAllowed(host))
|
||||
users_with_high_pl.insert(user);
|
||||
}
|
||||
for (const auto &user : create.content.additional_creators) {
|
||||
auto host = mtx::identifiers::parse<mtx::identifiers::User>(user).hostname();
|
||||
if (isHostAllowed(host))
|
||||
users_with_high_pl.insert(user);
|
||||
}
|
||||
}
|
||||
for (const auto &user : powerlevels.content.users) {
|
||||
if (user.second >= powerlevels.content.events_default &&
|
||||
user.second >= powerlevels.content.state_default) {
|
||||
|
|
@ -1525,12 +1541,13 @@ utils::roomVias(const std::string &roomid)
|
|||
});
|
||||
|
||||
// add the highest powerlevel user
|
||||
auto max_pl_user = std::max_element(
|
||||
users_with_high_pl_in_room.begin(),
|
||||
users_with_high_pl_in_room.end(),
|
||||
[&pl_content = powerlevels.content](const std::string &a, const std::string &b) {
|
||||
return pl_content.user_level(a) < pl_content.user_level(b);
|
||||
});
|
||||
auto max_pl_user = std::max_element(users_with_high_pl_in_room.begin(),
|
||||
users_with_high_pl_in_room.end(),
|
||||
[&pl_content = powerlevels.content, &create](
|
||||
const std::string &a, const std::string &b) {
|
||||
return pl_content.user_level(a, create) <
|
||||
pl_content.user_level(b, create);
|
||||
});
|
||||
if (max_pl_user != users_with_high_pl_in_room.end()) {
|
||||
auto host =
|
||||
mtx::identifiers::parse<mtx::identifiers::User>(*max_pl_user).hostname();
|
||||
|
|
@ -1705,11 +1722,15 @@ utils::updateSpaceVias()
|
|||
|
||||
auto spaceid = roomid.toStdString();
|
||||
|
||||
auto create = cache::client()->getStateEvent<mtx::events::state::Create>(spaceid).value_or(
|
||||
mtx::events::StateEvent<mtx::events::state::Create>{});
|
||||
|
||||
if (auto pl = cache::client()
|
||||
->getStateEvent<mtx::events::state::PowerLevels>(spaceid)
|
||||
.value_or(mtx::events::StateEvent<mtx::events::state::PowerLevels>{})
|
||||
.content;
|
||||
pl.user_level(us) < pl.state_level(to_string(mtx::events::EventType::SpaceChild)))
|
||||
pl.user_level(us, create) <
|
||||
pl.state_level(to_string(mtx::events::EventType::SpaceChild)))
|
||||
continue;
|
||||
|
||||
auto children = cache::client()->getChildRoomIds(spaceid);
|
||||
|
|
@ -1748,12 +1769,16 @@ utils::updateSpaceVias()
|
|||
parent->origin_server_ts < weekAgo &&
|
||||
// ignore unset spaces
|
||||
(parent->content.via && !parent->content.via->empty())) {
|
||||
auto childCreate =
|
||||
cache::client()->getStateEvent<mtx::events::state::Create>(spaceid).value_or(
|
||||
mtx::events::StateEvent<mtx::events::state::Create>{});
|
||||
|
||||
if (auto pl =
|
||||
cache::client()
|
||||
->getStateEvent<mtx::events::state::PowerLevels>(childid)
|
||||
.value_or(mtx::events::StateEvent<mtx::events::state::PowerLevels>{})
|
||||
.content;
|
||||
pl.user_level(us) <
|
||||
pl.user_level(us, childCreate) <
|
||||
pl.state_level(to_string(mtx::events::EventType::SpaceParent)))
|
||||
continue;
|
||||
|
||||
|
|
@ -2041,11 +2066,15 @@ utils::removeExpiredEvents()
|
|||
if (!asus->globalExpiry && !getExpEv(roomid))
|
||||
continue;
|
||||
|
||||
auto create = cache::client()->getStateEvent<mtx::events::state::Create>(roomid).value_or(
|
||||
mtx::events::StateEvent<mtx::events::state::Create>{});
|
||||
|
||||
if (auto pl = cache::client()
|
||||
->getStateEvent<mtx::events::state::PowerLevels>(roomid)
|
||||
.value_or(mtx::events::StateEvent<mtx::events::state::PowerLevels>{})
|
||||
.content;
|
||||
pl.user_level(us) < pl.event_level(to_string(mtx::events::EventType::RoomRedaction))) {
|
||||
pl.user_level(us, create) <
|
||||
pl.event_level(to_string(mtx::events::EventType::RoomRedaction))) {
|
||||
nhlog::net()->warn("Can't react events in {}, not running expiration.", roomid);
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
#include "Permissions.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "Cache_p.h"
|
||||
#include "MatrixClient.h"
|
||||
#include "TimelineModel.h"
|
||||
|
|
@ -22,50 +24,53 @@ Permissions::invalidate()
|
|||
->getStateEvent<mtx::events::state::PowerLevels>(roomId_.toStdString())
|
||||
.value_or(mtx::events::StateEvent<mtx::events::state::PowerLevels>{})
|
||||
.content;
|
||||
create = cache::client()
|
||||
->getStateEvent<mtx::events::state::Create>(roomId_.toStdString())
|
||||
.value_or(mtx::events::StateEvent<mtx::events::state::Create>{});
|
||||
}
|
||||
|
||||
bool
|
||||
Permissions::canInvite()
|
||||
{
|
||||
const bool plCheck = pl.user_level(http::client()->user_id().to_string()) >= pl.invite;
|
||||
return plCheck || this->isV12Creator();
|
||||
const bool plCheck = pl.user_level(http::client()->user_id().to_string(), create) >= pl.invite;
|
||||
return plCheck;
|
||||
}
|
||||
|
||||
bool
|
||||
Permissions::canBan()
|
||||
{
|
||||
const bool plCheck = pl.user_level(http::client()->user_id().to_string()) >= pl.ban;
|
||||
return plCheck || this->isV12Creator();
|
||||
const bool plCheck = pl.user_level(http::client()->user_id().to_string(), create) >= pl.ban;
|
||||
return plCheck;
|
||||
}
|
||||
|
||||
bool
|
||||
Permissions::canKick()
|
||||
{
|
||||
const bool plCheck = pl.user_level(http::client()->user_id().to_string()) >= pl.kick;
|
||||
return plCheck || this->isV12Creator();
|
||||
const bool plCheck = pl.user_level(http::client()->user_id().to_string(), create) >= pl.kick;
|
||||
return plCheck;
|
||||
}
|
||||
|
||||
bool
|
||||
Permissions::canRedact()
|
||||
{
|
||||
const bool plCheck = pl.user_level(http::client()->user_id().to_string()) >= pl.redact;
|
||||
return plCheck || this->isV12Creator();
|
||||
const bool plCheck = pl.user_level(http::client()->user_id().to_string(), create) >= pl.redact;
|
||||
return plCheck;
|
||||
}
|
||||
bool
|
||||
Permissions::canChange(int eventType)
|
||||
{
|
||||
const bool plCheck = pl.user_level(http::client()->user_id().to_string()) >=
|
||||
const bool plCheck = pl.user_level(http::client()->user_id().to_string(), create) >=
|
||||
pl.state_level(to_string(qml_mtx_events::fromRoomEventType(
|
||||
static_cast<qml_mtx_events::EventType>(eventType))));
|
||||
return plCheck || this->isV12Creator();
|
||||
return plCheck;
|
||||
}
|
||||
bool
|
||||
Permissions::canSend(int eventType)
|
||||
{
|
||||
const bool plCheck = pl.user_level(http::client()->user_id().to_string()) >=
|
||||
const bool plCheck = pl.user_level(http::client()->user_id().to_string(), create) >=
|
||||
pl.event_level(to_string(qml_mtx_events::fromRoomEventType(
|
||||
static_cast<qml_mtx_events::EventType>(eventType))));
|
||||
return plCheck || this->isV12Creator();
|
||||
return plCheck;
|
||||
}
|
||||
|
||||
int
|
||||
|
|
@ -94,15 +99,9 @@ Permissions::sendLevel(int eventType)
|
|||
bool
|
||||
Permissions::canPingRoom()
|
||||
{
|
||||
const bool plCheck = pl.user_level(http::client()->user_id().to_string()) >=
|
||||
const bool plCheck = pl.user_level(http::client()->user_id().to_string(), create) >=
|
||||
pl.notification_level(mtx::events::state::notification_keys::room);
|
||||
return plCheck || this->isV12Creator();
|
||||
return plCheck;
|
||||
}
|
||||
|
||||
bool
|
||||
Permissions::isV12Creator()
|
||||
{
|
||||
return cache::client()->isV12Creator(this->roomId_.toStdString(),
|
||||
http::client()->user_id().to_string());
|
||||
}
|
||||
#include "moc_Permissions.cpp"
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include <QObject>
|
||||
|
||||
#include <mtx/events.hpp>
|
||||
#include <mtx/events/create.hpp>
|
||||
#include <mtx/events/power_levels.hpp>
|
||||
|
||||
class TimelineModel;
|
||||
|
|
@ -28,16 +30,20 @@ public:
|
|||
Q_INVOKABLE int redactLevel();
|
||||
Q_INVOKABLE int changeLevel(int eventType);
|
||||
Q_INVOKABLE int sendLevel(int eventType);
|
||||
Q_INVOKABLE qint64 creatorLevel() const { return mtx::events::state::Creator; }
|
||||
|
||||
Q_INVOKABLE bool canPingRoom();
|
||||
|
||||
void invalidate();
|
||||
|
||||
const mtx::events::state::PowerLevels &powerlevelEvent() const { return pl; };
|
||||
const mtx::events::StateEvent<mtx::events::state::Create> &createEvent() const
|
||||
{
|
||||
return create;
|
||||
};
|
||||
|
||||
private:
|
||||
bool isV12Creator();
|
||||
|
||||
QString roomId_;
|
||||
mtx::events::state::PowerLevels pl;
|
||||
mtx::events::StateEvent<mtx::events::state::Create> create;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -605,8 +605,8 @@ TimelineModel::data(const mtx::events::collections::TimelineEvents &event, int r
|
|||
case UserName:
|
||||
return QVariant(displayName(QString::fromStdString(acc::sender(event))));
|
||||
case UserPowerlevel: {
|
||||
return static_cast<qlonglong>(
|
||||
permissions_.powerlevelEvent().user_level(acc::sender(event)));
|
||||
return static_cast<qlonglong>(permissions_.powerlevelEvent().user_level(
|
||||
acc::sender(event), permissions_.createEvent()));
|
||||
}
|
||||
|
||||
case Day: {
|
||||
|
|
@ -1413,12 +1413,6 @@ TimelineModel::readEvent(const std::string &id)
|
|||
!UserSettings::instance()->readReceipts());
|
||||
}
|
||||
|
||||
bool
|
||||
TimelineModel::isV12Creator(const QString &id) const
|
||||
{
|
||||
return cache::isV12Creator(this->roomId().toStdString(), id.toStdString());
|
||||
}
|
||||
|
||||
QString
|
||||
TimelineModel::displayName(const QString &id) const
|
||||
{
|
||||
|
|
@ -2427,6 +2421,7 @@ QString
|
|||
TimelineModel::formatPowerLevelEvent(
|
||||
const mtx::events::StateEvent<mtx::events::state::PowerLevels> &event) const
|
||||
{
|
||||
const auto create = permissions_.createEvent();
|
||||
mtx::events::StateEvent<mtx::events::state::PowerLevels> const *prevEvent = nullptr;
|
||||
if (!event.unsigned_data.replaces_state.empty()) {
|
||||
auto tempPrevEvent = events.get(event.unsigned_data.replaces_state, event.event_id);
|
||||
|
|
@ -2446,15 +2441,15 @@ TimelineModel::formatPowerLevelEvent(
|
|||
if (!prevEvent)
|
||||
return tr("%1 has changed the room's permissions.").arg(sender_name);
|
||||
|
||||
auto calc_affected = [&event,
|
||||
&prevEvent](int64_t newPowerlevelSetting) -> std::pair<QStringList, int> {
|
||||
auto calc_affected =
|
||||
[&event, &prevEvent, &create](int64_t newPowerlevelSetting) -> std::pair<QStringList, int> {
|
||||
QStringList affected{};
|
||||
auto numberOfAffected = 0;
|
||||
// We do only compare to people with explicit PL. Usually others are not going to be
|
||||
// affected either way and this is cheaper to iterate over.
|
||||
for (auto const &[mxid, currentPowerlevel] : event.content.users) {
|
||||
if (currentPowerlevel == newPowerlevelSetting &&
|
||||
prevEvent->content.user_level(mxid) < newPowerlevelSetting) {
|
||||
prevEvent->content.user_level(mxid, create) < newPowerlevelSetting) {
|
||||
numberOfAffected++;
|
||||
if (numberOfAffected <= 2) {
|
||||
affected.push_back(QString::fromStdString(mxid));
|
||||
|
|
@ -2631,24 +2626,25 @@ TimelineModel::formatPowerLevelEvent(
|
|||
// Compare if a Powerlevel of a user changed
|
||||
for (auto const &[mxid, powerlevel] : event.content.users) {
|
||||
auto nameOfChangedUser = utils::replaceEmoji(displayName(QString::fromStdString(mxid)));
|
||||
if (prevEvent->content.user_level(mxid) != powerlevel) {
|
||||
if (prevEvent->content.user_level(mxid, create) != powerlevel) {
|
||||
if (powerlevel >= administrator_power_level) {
|
||||
resultingMessage.append(tr("%1 has made %2 an administrator of this room.")
|
||||
.arg(sender_name, nameOfChangedUser));
|
||||
} else if (powerlevel >= moderator_power_level &&
|
||||
powerlevel > prevEvent->content.user_level(mxid)) {
|
||||
powerlevel > prevEvent->content.user_level(mxid, create)) {
|
||||
resultingMessage.append(tr("%1 has made %2 a moderator of this room.")
|
||||
.arg(sender_name, nameOfChangedUser));
|
||||
} else if (powerlevel >= moderator_power_level &&
|
||||
powerlevel < prevEvent->content.user_level(mxid)) {
|
||||
powerlevel < prevEvent->content.user_level(mxid, create)) {
|
||||
resultingMessage.append(tr("%1 has downgraded %2 to moderator of this room.")
|
||||
.arg(sender_name, nameOfChangedUser));
|
||||
} else {
|
||||
resultingMessage.append(tr("%1 has changed the powerlevel of %2 from %3 to %4.")
|
||||
.arg(sender_name,
|
||||
nameOfChangedUser,
|
||||
QString::number(prevEvent->content.user_level(mxid)),
|
||||
QString::number(powerlevel)));
|
||||
resultingMessage.append(
|
||||
tr("%1 has changed the powerlevel of %2 from %3 to %4.")
|
||||
.arg(sender_name,
|
||||
nameOfChangedUser,
|
||||
QString::number(prevEvent->content.user_level(mxid, create)),
|
||||
QString::number(powerlevel)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3379,6 +3375,7 @@ TimelineModel::pushrulesRoomContext() const
|
|||
cache::displayName(room_id_.toStdString(), http::client()->user_id().to_string()),
|
||||
.member_count = cache::client()->memberCount(room_id_.toStdString()),
|
||||
.power_levels = permissions_.powerlevelEvent(),
|
||||
.create = permissions_.createEvent(),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -302,7 +302,6 @@ public:
|
|||
static QString getBareRoomLink(const QString &);
|
||||
static QString getRoomVias(const QString &);
|
||||
|
||||
Q_INVOKABLE bool isV12Creator(const QString &id) const;
|
||||
Q_INVOKABLE QString displayName(const QString &id) const;
|
||||
Q_INVOKABLE QString avatarUrl(const QString &id) const;
|
||||
Q_INVOKABLE QString formatDateSeparator(QDate date) const;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue