|
|
|
@ -1934,11 +1934,268 @@ TimelineModel::formatPowerLevelEvent(const QString &id) |
|
|
|
|
if (!event) |
|
|
|
|
return QString(); |
|
|
|
|
|
|
|
|
|
mtx::events::StateEvent<mtx::events::state::PowerLevels> *prevEvent = nullptr; |
|
|
|
|
if (!event->unsigned_data.replaces_state.empty()) { |
|
|
|
|
auto tempPrevEvent = events.get(event->unsigned_data.replaces_state, event->event_id); |
|
|
|
|
if (tempPrevEvent) { |
|
|
|
|
prevEvent = |
|
|
|
|
std::get_if<mtx::events::StateEvent<mtx::events::state::PowerLevels>>(tempPrevEvent); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
QString user = QString::fromStdString(event->sender); |
|
|
|
|
QString name = utils::replaceEmoji(displayName(user)); |
|
|
|
|
QString sender_name = utils::replaceEmoji(displayName(user)); |
|
|
|
|
// Get the rooms levels for redactions and powerlevel changes to determine "Administrator" and
|
|
|
|
|
// "Moderator" powerlevels.
|
|
|
|
|
auto administrator_power_level = event->content.state_level("m.room.power_levels"); |
|
|
|
|
auto moderator_power_level = event->content.redact; |
|
|
|
|
auto default_powerlevel = event->content.users_default; |
|
|
|
|
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> { |
|
|
|
|
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) { |
|
|
|
|
numberOfAffected++; |
|
|
|
|
if (numberOfAffected <= 2) { |
|
|
|
|
affected.push_back(QString::fromStdString(mxid)); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return {affected, numberOfAffected}; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
QStringList resultingMessage{}; |
|
|
|
|
// These affect only a few people. Therefor we can print who is affected.
|
|
|
|
|
if (event->content.kick != prevEvent->content.kick) { |
|
|
|
|
auto default_message = tr("%1 has changed the room's kick powerlevel from %2 to %3.") |
|
|
|
|
.arg(sender_name) |
|
|
|
|
.arg(prevEvent->content.kick) |
|
|
|
|
.arg(event->content.kick); |
|
|
|
|
|
|
|
|
|
// We only calculate affected users if we change to a level above the default users PL
|
|
|
|
|
// to not accidentally have a DoS vector
|
|
|
|
|
if (event->content.kick > default_powerlevel) { |
|
|
|
|
auto [affected, number_of_affected] = calc_affected(event->content.kick); |
|
|
|
|
|
|
|
|
|
if (number_of_affected != 0) { |
|
|
|
|
auto true_affected_rest = number_of_affected - affected.size(); |
|
|
|
|
if (number_of_affected > 1) { |
|
|
|
|
resultingMessage.append( |
|
|
|
|
default_message + QStringLiteral(" ") + |
|
|
|
|
tr("%n member(s) can now kick room members.", nullptr, true_affected_rest)); |
|
|
|
|
} else if (number_of_affected == 1) { |
|
|
|
|
resultingMessage.append( |
|
|
|
|
default_message + QStringLiteral(" ") + |
|
|
|
|
tr("%1 can now kick room members.") |
|
|
|
|
.arg(utils::replaceEmoji(displayName(affected.at(0))))); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
resultingMessage.append(default_message); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
resultingMessage.append(default_message); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (event->content.redact != prevEvent->content.redact) { |
|
|
|
|
auto default_message = tr("%1 has changed the room's redact powerlevel from %2 to %3.") |
|
|
|
|
.arg(sender_name) |
|
|
|
|
.arg(prevEvent->content.redact) |
|
|
|
|
.arg(event->content.redact); |
|
|
|
|
|
|
|
|
|
// We only calculate affected users if we change to a level above the default users PL
|
|
|
|
|
// to not accidentally have a DoS vector
|
|
|
|
|
if (event->content.redact > default_powerlevel) { |
|
|
|
|
auto [affected, number_of_affected] = calc_affected(event->content.redact); |
|
|
|
|
|
|
|
|
|
if (number_of_affected != 0) { |
|
|
|
|
auto true_affected_rest = number_of_affected - affected.size(); |
|
|
|
|
if (number_of_affected > 1) { |
|
|
|
|
resultingMessage.append( |
|
|
|
|
default_message + QStringLiteral(" ") + |
|
|
|
|
tr("%n member(s) can now redact room messages.", nullptr, true_affected_rest)); |
|
|
|
|
} else if (number_of_affected == 1) { |
|
|
|
|
resultingMessage.append( |
|
|
|
|
default_message + QStringLiteral(" ") + |
|
|
|
|
tr("%1 can now redact room messages.") |
|
|
|
|
.arg(utils::replaceEmoji(displayName(affected.at(0))))); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
resultingMessage.append(default_message); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
resultingMessage.append(default_message); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (event->content.ban != prevEvent->content.ban) { |
|
|
|
|
auto default_message = tr("%1 has changed the room's ban powerlevel from %2 to %3.") |
|
|
|
|
.arg(sender_name) |
|
|
|
|
.arg(prevEvent->content.ban) |
|
|
|
|
.arg(event->content.ban); |
|
|
|
|
|
|
|
|
|
// We only calculate affected users if we change to a level above the default users PL
|
|
|
|
|
// to not accidentally have a DoS vector
|
|
|
|
|
if (event->content.ban > default_powerlevel) { |
|
|
|
|
auto [affected, number_of_affected] = calc_affected(event->content.ban); |
|
|
|
|
|
|
|
|
|
if (number_of_affected != 0) { |
|
|
|
|
auto true_affected_rest = number_of_affected - affected.size(); |
|
|
|
|
if (number_of_affected > 1) { |
|
|
|
|
resultingMessage.append( |
|
|
|
|
default_message + QStringLiteral(" ") + |
|
|
|
|
tr("%n member(s) can now ban room members.", nullptr, true_affected_rest)); |
|
|
|
|
} else if (number_of_affected == 1) { |
|
|
|
|
resultingMessage.append( |
|
|
|
|
default_message + QStringLiteral(" ") + |
|
|
|
|
tr("%1 can now ban room members.") |
|
|
|
|
.arg(utils::replaceEmoji(displayName(affected.at(0))))); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
resultingMessage.append(default_message); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
resultingMessage.append(default_message); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (event->content.state_default != prevEvent->content.state_default) { |
|
|
|
|
auto default_message = |
|
|
|
|
tr("%1 has changed the room's state_default powerlevel from %2 to %3.") |
|
|
|
|
.arg(sender_name) |
|
|
|
|
.arg(prevEvent->content.state_default) |
|
|
|
|
.arg(event->content.state_default); |
|
|
|
|
|
|
|
|
|
// We only calculate affected users if we change to a level above the default users PL
|
|
|
|
|
// to not accidentally have a DoS vector
|
|
|
|
|
if (event->content.state_default > default_powerlevel) { |
|
|
|
|
auto [affected, number_of_affected] = calc_affected(event->content.kick); |
|
|
|
|
|
|
|
|
|
if (number_of_affected != 0) { |
|
|
|
|
auto true_affected_rest = number_of_affected - affected.size(); |
|
|
|
|
if (number_of_affected > 1) { |
|
|
|
|
resultingMessage.append( |
|
|
|
|
default_message + QStringLiteral(" ") + |
|
|
|
|
tr("%n member(s) can now send state events.", nullptr, true_affected_rest)); |
|
|
|
|
} else if (number_of_affected == 1) { |
|
|
|
|
resultingMessage.append( |
|
|
|
|
default_message + QStringLiteral(" ") + |
|
|
|
|
tr("%1 can now send state events.") |
|
|
|
|
.arg(utils::replaceEmoji(displayName(affected.at(0))))); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
resultingMessage.append(default_message); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
resultingMessage.append(default_message); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// These affect potentially the whole room. We there for do not calculate who gets affected
|
|
|
|
|
// by this to prevent huge lists of people.
|
|
|
|
|
if (event->content.invite != prevEvent->content.invite) { |
|
|
|
|
resultingMessage.append(tr("%1 has changed the room's invite powerlevel from %2 to %3.") |
|
|
|
|
.arg(sender_name, |
|
|
|
|
QString::number(prevEvent->content.invite), |
|
|
|
|
QString::number(event->content.invite))); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (event->content.events_default != prevEvent->content.events_default) { |
|
|
|
|
if ((event->content.events_default > default_powerlevel) && |
|
|
|
|
prevEvent->content.events_default <= default_powerlevel) { |
|
|
|
|
resultingMessage.append( |
|
|
|
|
tr("%1 has changed the room's events_default powerlevel from %2 to %3. New " |
|
|
|
|
"users can now not send any events.") |
|
|
|
|
.arg(sender_name, |
|
|
|
|
QString::number(prevEvent->content.events_default), |
|
|
|
|
QString::number(event->content.events_default))); |
|
|
|
|
} else if ((event->content.events_default < prevEvent->content.events_default) && |
|
|
|
|
(event->content.events_default < default_powerlevel) && |
|
|
|
|
(prevEvent->content.events_default > default_powerlevel)) { |
|
|
|
|
resultingMessage.append( |
|
|
|
|
tr("%1 has changed the room's events_default powerlevel from %2 to %3. New " |
|
|
|
|
"users can now send events that are not otherwise restricted.") |
|
|
|
|
.arg(sender_name, |
|
|
|
|
QString::number(prevEvent->content.events_default), |
|
|
|
|
QString::number(event->content.events_default))); |
|
|
|
|
} else { |
|
|
|
|
resultingMessage.append( |
|
|
|
|
tr("%1 has changed the room's events_default powerlevel from %2 to %3.") |
|
|
|
|
.arg(sender_name, |
|
|
|
|
QString::number(prevEvent->content.events_default), |
|
|
|
|
QString::number(event->content.events_default))); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 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 (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)) { |
|
|
|
|
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)) { |
|
|
|
|
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))); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Handle added/removed/changed event type
|
|
|
|
|
for (auto const &[event_type, powerlevel] : event->content.events) { |
|
|
|
|
auto prev_not_present = |
|
|
|
|
prevEvent->content.events.find(event_type) == prevEvent->content.events.end(); |
|
|
|
|
|
|
|
|
|
if (prev_not_present || prevEvent->content.events.at(event_type) != powerlevel) { |
|
|
|
|
if (powerlevel >= administrator_power_level) { |
|
|
|
|
resultingMessage.append(tr("%1 allowed only administrators to send \"%2\".") |
|
|
|
|
.arg(sender_name, QString::fromStdString(event_type))); |
|
|
|
|
} else if (powerlevel >= moderator_power_level) { |
|
|
|
|
resultingMessage.append(tr("%1 allowed only moderators to send \"%2\".") |
|
|
|
|
.arg(sender_name, QString::fromStdString(event_type))); |
|
|
|
|
} else if (powerlevel == default_powerlevel) { |
|
|
|
|
resultingMessage.append(tr("%1 allowed everyone to send \"%2\".") |
|
|
|
|
.arg(sender_name, QString::fromStdString(event_type))); |
|
|
|
|
} else if (prev_not_present) { |
|
|
|
|
resultingMessage.append( |
|
|
|
|
tr("%1 has changed the powerlevel of event type \"%2\" from the default to %3.") |
|
|
|
|
.arg(sender_name, |
|
|
|
|
QString::fromStdString(event_type), |
|
|
|
|
QString::number(powerlevel))); |
|
|
|
|
} else { |
|
|
|
|
resultingMessage.append( |
|
|
|
|
tr("%1 has changed the powerlevel of event type \"%2\" from %3 to %4.") |
|
|
|
|
.arg(sender_name, |
|
|
|
|
QString::fromStdString(event_type), |
|
|
|
|
QString::number(prevEvent->content.events.at(event_type)), |
|
|
|
|
QString::number(powerlevel))); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// TODO: power levels rendering is actually a bit complex. work on this later.
|
|
|
|
|
return tr("%1 has changed the room's permissions.").arg(name); |
|
|
|
|
if (!resultingMessage.isEmpty()) { |
|
|
|
|
return resultingMessage.join("<br/>"); |
|
|
|
|
} else { |
|
|
|
|
return tr("%1 has changed the room's permissions.").arg(sender_name); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
QVariantMap |
|
|
|
|