mirror of
https://github.com/overte-org/overte.git
synced 2025-08-06 13:49:46 +02:00
Merge pull request #9426 from zfox23/PAL_AdminLabel
PAL: Admin Labels, Updated Letterboxes
This commit is contained in:
commit
64ef3d5665
8 changed files with 242 additions and 95 deletions
|
@ -782,41 +782,48 @@ void DomainServerSettingsManager::processNodeKickRequestPacket(QSharedPointer<Re
|
||||||
void DomainServerSettingsManager::processUsernameFromIDRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
void DomainServerSettingsManager::processUsernameFromIDRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer sendingNode) {
|
||||||
// From the packet, pull the UUID we're identifying
|
// From the packet, pull the UUID we're identifying
|
||||||
QUuid nodeUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
QUuid nodeUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID));
|
||||||
|
|
||||||
if (!nodeUUID.isNull()) {
|
if (!nodeUUID.isNull()) {
|
||||||
// Before we do any processing on this packet, make sure it comes from a node that is allowed to kick (is an admin)
|
// First, make sure we actually have a node with this UUID
|
||||||
// OR from a node whose UUID matches the one in the packet
|
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
||||||
if (sendingNode->getCanKick() || nodeUUID == sendingNode->getUUID()) {
|
auto matchingNode = limitedNodeList->nodeWithUUID(nodeUUID);
|
||||||
// First, make sure we actually have a node with this UUID
|
|
||||||
auto limitedNodeList = DependencyManager::get<LimitedNodeList>();
|
|
||||||
auto matchingNode = limitedNodeList->nodeWithUUID(nodeUUID);
|
|
||||||
|
|
||||||
// If we do have a matching node...
|
// If we do have a matching node...
|
||||||
if (matchingNode) {
|
if (matchingNode) {
|
||||||
|
// Setup the packet
|
||||||
|
auto usernameFromIDReplyPacket = NLPacket::create(PacketType::UsernameFromIDReply);
|
||||||
|
|
||||||
|
QString verifiedUsername;
|
||||||
|
QUuid machineFingerprint;
|
||||||
|
|
||||||
|
// Write the UUID to the packet
|
||||||
|
usernameFromIDReplyPacket->write(nodeUUID.toRfc4122());
|
||||||
|
|
||||||
|
// Check if the sending node has permission to kick (is an admin)
|
||||||
|
// OR if the message is from a node whose UUID matches the one in the packet
|
||||||
|
if (sendingNode->getCanKick() || nodeUUID == sendingNode->getUUID()) {
|
||||||
// It's time to figure out the username
|
// It's time to figure out the username
|
||||||
QString verifiedUsername = matchingNode->getPermissions().getVerifiedUserName();
|
verifiedUsername = matchingNode->getPermissions().getVerifiedUserName();
|
||||||
|
|
||||||
// Setup the packet
|
|
||||||
auto usernameFromIDReplyPacket = NLPacket::create(PacketType::UsernameFromIDReply);
|
|
||||||
usernameFromIDReplyPacket->write(nodeUUID.toRfc4122());
|
|
||||||
usernameFromIDReplyPacket->writeString(verifiedUsername);
|
usernameFromIDReplyPacket->writeString(verifiedUsername);
|
||||||
|
|
||||||
// now put in the machine fingerprint
|
// now put in the machine fingerprint
|
||||||
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(matchingNode->getLinkedData());
|
DomainServerNodeData* nodeData = reinterpret_cast<DomainServerNodeData*>(matchingNode->getLinkedData());
|
||||||
QUuid machineFingerprint = nodeData ? nodeData->getMachineFingerprint() : QUuid();
|
machineFingerprint = nodeData ? nodeData->getMachineFingerprint() : QUuid();
|
||||||
usernameFromIDReplyPacket->write(machineFingerprint.toRfc4122());
|
usernameFromIDReplyPacket->write(machineFingerprint.toRfc4122());
|
||||||
|
|
||||||
qDebug() << "Sending username" << verifiedUsername << "and machine fingerprint" << machineFingerprint << "associated with node" << nodeUUID;
|
|
||||||
|
|
||||||
// Ship it!
|
|
||||||
limitedNodeList->sendPacket(std::move(usernameFromIDReplyPacket), *sendingNode);
|
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "Node username request received for unknown node. Refusing to process.";
|
usernameFromIDReplyPacket->writeString(verifiedUsername);
|
||||||
|
usernameFromIDReplyPacket->write(machineFingerprint.toRfc4122());
|
||||||
}
|
}
|
||||||
} else {
|
// Write whether or not the user is an admin
|
||||||
qWarning() << "Refusing to process a username request packet from node" << uuidStringWithoutCurlyBraces(sendingNode->getUUID())
|
bool isAdmin = matchingNode->getCanKick();
|
||||||
<< ". Either node doesn't have kick permissions or is requesting a username not from their UUID.";
|
usernameFromIDReplyPacket->writePrimitive(isAdmin);
|
||||||
}
|
|
||||||
|
|
||||||
|
qDebug() << "Sending username" << verifiedUsername << "and machine fingerprint" << machineFingerprint << "associated with node" << nodeUUID << ". Node admin status: " << isAdmin;
|
||||||
|
// Ship it!
|
||||||
|
limitedNodeList->sendPacket(std::move(usernameFromIDReplyPacket), *sendingNode);
|
||||||
|
} else {
|
||||||
|
qWarning() << "Node username request received for unknown node. Refusing to process.";
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "Node username request received for invalid node ID. Refusing to process.";
|
qWarning() << "Node username request received for invalid node ID. Refusing to process.";
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,13 @@ import "../styles-uit"
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
property alias text: popupText.text
|
property alias text: popupText.text
|
||||||
|
property alias headerGlyph: headerGlyph.text
|
||||||
|
property alias headerText: headerText.text
|
||||||
property real popupRadius: hifi.dimensions.borderRadius
|
property real popupRadius: hifi.dimensions.borderRadius
|
||||||
|
property real headerTextPixelSize: 22
|
||||||
|
property real popupTextPixelSize: 16
|
||||||
|
FontLoader { id: ralewayRegular; source: "../../fonts/Raleway-Regular.ttf"; }
|
||||||
|
FontLoader { id: ralewaySemiBold; source: "../../fonts/Raleway-SemiBold.ttf"; }
|
||||||
visible: false
|
visible: false
|
||||||
id: letterbox
|
id: letterbox
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
@ -27,19 +33,79 @@ Item {
|
||||||
}
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
width: Math.max(parent.width * 0.75, 400)
|
width: Math.max(parent.width * 0.75, 400)
|
||||||
height: popupText.contentHeight*1.5
|
height: contentContainer.height + 50
|
||||||
anchors.centerIn: parent
|
anchors.centerIn: parent
|
||||||
radius: popupRadius
|
radius: popupRadius
|
||||||
color: "white"
|
color: "white"
|
||||||
FiraSansSemiBold {
|
Item {
|
||||||
id: popupText
|
id: contentContainer
|
||||||
size: hifi.fontSizes.textFieldInput
|
width: parent.width - 60
|
||||||
color: hifi.colors.darkGray
|
height: childrenRect.height
|
||||||
horizontalAlignment: Text.AlignHCenter
|
anchors.centerIn: parent
|
||||||
anchors.fill: parent
|
Item {
|
||||||
anchors.leftMargin: 15
|
id: popupHeaderContainer
|
||||||
anchors.rightMargin: 15
|
visible: headerText.text !== "" || headerGlyph.text !== ""
|
||||||
wrapMode: Text.WordWrap
|
height: 30
|
||||||
|
// Anchors
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
// Header Glyph
|
||||||
|
HiFiGlyphs {
|
||||||
|
id: headerGlyph
|
||||||
|
visible: headerGlyph.text !== ""
|
||||||
|
// Size
|
||||||
|
height: parent.height
|
||||||
|
// Anchors
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.leftMargin: -15
|
||||||
|
// Text Size
|
||||||
|
size: headerTextPixelSize*2.5
|
||||||
|
// Style
|
||||||
|
horizontalAlignment: Text.AlignHLeft
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: hifi.colors.darkGray
|
||||||
|
}
|
||||||
|
// Header Text
|
||||||
|
Text {
|
||||||
|
id: headerText
|
||||||
|
visible: headerText.text !== ""
|
||||||
|
// Size
|
||||||
|
height: parent.height
|
||||||
|
// Anchors
|
||||||
|
anchors.left: headerGlyph.right
|
||||||
|
anchors.leftMargin: -5
|
||||||
|
// Text Size
|
||||||
|
font.pixelSize: headerTextPixelSize
|
||||||
|
// Style
|
||||||
|
font.family: ralewaySemiBold.name
|
||||||
|
color: hifi.colors.darkGray
|
||||||
|
horizontalAlignment: Text.AlignHLeft
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
textFormat: Text.StyledText
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Popup Text
|
||||||
|
Text {
|
||||||
|
id: popupText
|
||||||
|
// Size
|
||||||
|
width: parent.width
|
||||||
|
// Anchors
|
||||||
|
anchors.top: popupHeaderContainer.visible ? popupHeaderContainer.bottom : parent.top
|
||||||
|
anchors.topMargin: popupHeaderContainer.visible ? 15 : 0
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
// Text alignment
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
horizontalAlignment: Text.AlignHLeft
|
||||||
|
// Style
|
||||||
|
font.pixelSize: popupTextPixelSize
|
||||||
|
font.family: ralewayRegular.name
|
||||||
|
color: hifi.colors.darkGray
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
textFormat: Text.StyledText
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MouseArea {
|
MouseArea {
|
||||||
|
|
|
@ -33,6 +33,7 @@ Item {
|
||||||
property real audioLevel: 0.0
|
property real audioLevel: 0.0
|
||||||
property bool isMyCard: false
|
property bool isMyCard: false
|
||||||
property bool selected: false
|
property bool selected: false
|
||||||
|
property bool isAdmin: false
|
||||||
|
|
||||||
/* User image commented out for now - will probably be re-introduced later.
|
/* User image commented out for now - will probably be re-introduced later.
|
||||||
Column {
|
Column {
|
||||||
|
@ -139,32 +140,94 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Spacer for DisplayName for my card
|
// Spacer for DisplayName for my card
|
||||||
Rectangle {
|
Item {
|
||||||
id: myDisplayNameSpacer
|
id: myDisplayNameSpacer
|
||||||
width: myDisplayName.width
|
width: 1
|
||||||
|
height: 4
|
||||||
// Anchors
|
// Anchors
|
||||||
anchors.top: myDisplayName.bottom
|
anchors.top: myDisplayName.bottom
|
||||||
height: 5
|
|
||||||
visible: isMyCard
|
|
||||||
opacity: 0
|
|
||||||
}
|
}
|
||||||
// DisplayName Text for others' cards
|
// DisplayName container for others' cards
|
||||||
FiraSansSemiBold {
|
Item {
|
||||||
id: displayNameText
|
id: displayNameContainer
|
||||||
// Properties
|
|
||||||
text: thisNameCard.displayName
|
|
||||||
elide: Text.ElideRight
|
|
||||||
visible: !isMyCard
|
visible: !isMyCard
|
||||||
// Size
|
// Size
|
||||||
width: parent.width
|
width: parent.width
|
||||||
|
height: displayNameTextPixelSize + 4
|
||||||
// Anchors
|
// Anchors
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
// Text Size
|
anchors.left: parent.left
|
||||||
size: displayNameTextPixelSize
|
// DisplayName Text for others' cards
|
||||||
// Text Positioning
|
FiraSansSemiBold {
|
||||||
verticalAlignment: Text.AlignVCenter
|
id: displayNameText
|
||||||
// Style
|
// Properties
|
||||||
color: hifi.colors.darkGray
|
text: thisNameCard.displayName
|
||||||
|
elide: Text.ElideRight
|
||||||
|
// Size
|
||||||
|
width: isAdmin ? Math.min(displayNameTextMetrics.tightBoundingRect.width + 8, parent.width - adminLabelText.width - adminLabelQuestionMark.width + 8) : parent.width
|
||||||
|
// Anchors
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.left: parent.left
|
||||||
|
// Text Size
|
||||||
|
size: displayNameTextPixelSize
|
||||||
|
// Text Positioning
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
// Style
|
||||||
|
color: hifi.colors.darkGray
|
||||||
|
}
|
||||||
|
TextMetrics {
|
||||||
|
id: displayNameTextMetrics
|
||||||
|
font: displayNameText.font
|
||||||
|
text: displayNameText.text
|
||||||
|
}
|
||||||
|
// "ADMIN" label for other users' cards
|
||||||
|
RalewaySemiBold {
|
||||||
|
id: adminLabelText
|
||||||
|
visible: isAdmin
|
||||||
|
text: "ADMIN"
|
||||||
|
// Text size
|
||||||
|
size: displayNameText.size - 4
|
||||||
|
// Anchors
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: displayNameText.right
|
||||||
|
// Style
|
||||||
|
font.capitalization: Font.AllUppercase
|
||||||
|
color: hifi.colors.redHighlight
|
||||||
|
// Alignment
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignTop
|
||||||
|
}
|
||||||
|
// This Rectangle refers to the [?] popup button next to "ADMIN"
|
||||||
|
Item {
|
||||||
|
id: adminLabelQuestionMark
|
||||||
|
visible: isAdmin
|
||||||
|
// Size
|
||||||
|
width: 20
|
||||||
|
height: displayNameText.height
|
||||||
|
// Anchors
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: adminLabelText.right
|
||||||
|
RalewayRegular {
|
||||||
|
id: adminLabelQuestionMarkText
|
||||||
|
text: "[?]"
|
||||||
|
size: adminLabelText.size
|
||||||
|
font.capitalization: Font.AllUppercase
|
||||||
|
color: hifi.colors.redHighlight
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
MouseArea {
|
||||||
|
anchors.fill: parent
|
||||||
|
acceptedButtons: Qt.LeftButton
|
||||||
|
hoverEnabled: true
|
||||||
|
onClicked: letterbox(hifi.glyphs.question,
|
||||||
|
"Domain Admin",
|
||||||
|
"This user is an admin on this domain. Admins can <b>Silence</b> and <b>Ban</b> other users at their discretion - so be extra nice!")
|
||||||
|
onEntered: adminLabelQuestionMarkText.color = "#94132e"
|
||||||
|
onExited: adminLabelQuestionMarkText.color = hifi.colors.redHighlight
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserName Text
|
// UserName Text
|
||||||
|
@ -177,7 +240,7 @@ Item {
|
||||||
// Size
|
// Size
|
||||||
width: parent.width
|
width: parent.width
|
||||||
// Anchors
|
// Anchors
|
||||||
anchors.top: isMyCard ? myDisplayNameSpacer.bottom : displayNameText.bottom
|
anchors.top: isMyCard ? myDisplayNameSpacer.bottom : displayNameContainer.bottom
|
||||||
// Text Size
|
// Text Size
|
||||||
size: thisNameCard.usernameTextHeight
|
size: thisNameCard.usernameTextHeight
|
||||||
// Text Positioning
|
// Text Positioning
|
||||||
|
@ -188,7 +251,7 @@ Item {
|
||||||
|
|
||||||
// Spacer
|
// Spacer
|
||||||
Item {
|
Item {
|
||||||
id: spacer
|
id: userNameSpacer
|
||||||
height: 4
|
height: 4
|
||||||
width: parent.width
|
width: parent.width
|
||||||
// Anchors
|
// Anchors
|
||||||
|
@ -199,10 +262,10 @@ Item {
|
||||||
Rectangle {
|
Rectangle {
|
||||||
id: nameCardVUMeter
|
id: nameCardVUMeter
|
||||||
// Size
|
// Size
|
||||||
width: isMyCard ? myDisplayName.width - 20 : ((gainSlider.value - gainSlider.minimumValue)/(gainSlider.maximumValue - gainSlider.minimumValue)) * parent.width
|
width: isMyCard ? myDisplayName.width - 70 : ((gainSlider.value - gainSlider.minimumValue)/(gainSlider.maximumValue - gainSlider.minimumValue)) * parent.width
|
||||||
height: 8
|
height: 8
|
||||||
// Anchors
|
// Anchors
|
||||||
anchors.top: spacer.bottom
|
anchors.top: userNameSpacer.bottom
|
||||||
// Style
|
// Style
|
||||||
radius: 4
|
radius: 4
|
||||||
color: "#c5c5c5"
|
color: "#c5c5c5"
|
||||||
|
|
|
@ -28,7 +28,7 @@ Rectangle {
|
||||||
property int rowHeight: 70
|
property int rowHeight: 70
|
||||||
property int actionButtonWidth: 75
|
property int actionButtonWidth: 75
|
||||||
property int nameCardWidth: palContainer.width - actionButtonWidth*(iAmAdmin ? 4 : 2) - 4 - hifi.dimensions.scrollbarBackgroundWidth
|
property int nameCardWidth: palContainer.width - actionButtonWidth*(iAmAdmin ? 4 : 2) - 4 - hifi.dimensions.scrollbarBackgroundWidth
|
||||||
property var myData: ({displayName: "", userName: "", audioLevel: 0.0}) // valid dummy until set
|
property var myData: ({displayName: "", userName: "", audioLevel: 0.0, admin: true}) // valid dummy until set
|
||||||
property var ignored: ({}); // Keep a local list of ignored avatars & their data. Necessary because HashMap is slow to respond after ignoring.
|
property var ignored: ({}); // Keep a local list of ignored avatars & their data. Necessary because HashMap is slow to respond after ignoring.
|
||||||
property var userModelData: [] // This simple list is essentially a mirror of the userModel listModel without all the extra complexities.
|
property var userModelData: [] // This simple list is essentially a mirror of the userModel listModel without all the extra complexities.
|
||||||
property bool iAmAdmin: false
|
property bool iAmAdmin: false
|
||||||
|
@ -41,7 +41,9 @@ Rectangle {
|
||||||
id: letterboxMessage
|
id: letterboxMessage
|
||||||
z: 999 // Force the popup on top of everything else
|
z: 999 // Force the popup on top of everything else
|
||||||
}
|
}
|
||||||
function letterbox(message) {
|
function letterbox(headerGlyph, headerText, message) {
|
||||||
|
letterboxMessage.headerGlyph = headerGlyph
|
||||||
|
letterboxMessage.headerText = headerText
|
||||||
letterboxMessage.text = message
|
letterboxMessage.text = message
|
||||||
letterboxMessage.visible = true
|
letterboxMessage.visible = true
|
||||||
letterboxMessage.popupRadius = 0
|
letterboxMessage.popupRadius = 0
|
||||||
|
@ -221,6 +223,7 @@ Rectangle {
|
||||||
visible: !isCheckBox && !isButton
|
visible: !isCheckBox && !isButton
|
||||||
uuid: model && model.sessionId
|
uuid: model && model.sessionId
|
||||||
selected: styleData.selected
|
selected: styleData.selected
|
||||||
|
isAdmin: model && model.admin
|
||||||
// Size
|
// Size
|
||||||
width: nameCardWidth
|
width: nameCardWidth
|
||||||
height: parent.height
|
height: parent.height
|
||||||
|
@ -369,9 +372,11 @@ Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
acceptedButtons: Qt.LeftButton
|
acceptedButtons: Qt.LeftButton
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
onClicked: letterbox("Bold names in the list are Avatar Display Names.\n" +
|
onClicked: letterbox(hifi.glyphs.question,
|
||||||
"If a Display Name isn't set, a unique Session Display Name is assigned." +
|
"Display Names",
|
||||||
"\n\nAdministrators of this domain can also see the Username or Machine ID associated with each avatar present.")
|
"Bold names in the list are <b>avatar display names</b>.<br>" +
|
||||||
|
"If a display name isn't set, a unique <b>session display name</b> is assigned." +
|
||||||
|
"<br><br>Administrators of this domain can also see the <b>username</b> or <b>machine ID</b> associated with each avatar present.")
|
||||||
onEntered: helpText.color = hifi.colors.baseGrayHighlight
|
onEntered: helpText.color = hifi.colors.baseGrayHighlight
|
||||||
onExited: helpText.color = hifi.colors.darkGray
|
onExited: helpText.color = hifi.colors.darkGray
|
||||||
}
|
}
|
||||||
|
@ -400,8 +405,10 @@ Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
acceptedButtons: Qt.LeftButton
|
acceptedButtons: Qt.LeftButton
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
onClicked: letterbox('Silencing a user mutes their microphone. Silenced users can unmute themselves by clicking the "UNMUTE" button on their HUD.\n\n' +
|
onClicked: letterbox(hifi.glyphs.question,
|
||||||
"Banning a user will remove them from this domain and prevent them from returning. You can un-ban users from your domain's settings page.)")
|
"Admin Actions",
|
||||||
|
"<b>Silence</b> mutes a user's microphone. Silenced users can unmute themselves by clicking "UNMUTE" on their toolbar.<br><br>" +
|
||||||
|
"<b>Ban</b> removes a user from this domain and prevents them from returning. Admins can un-ban users from the Sandbox Domain Settings page.")
|
||||||
onEntered: adminHelpText.color = "#94132e"
|
onEntered: adminHelpText.color = "#94132e"
|
||||||
onExited: adminHelpText.color = hifi.colors.redHighlight
|
onExited: adminHelpText.color = hifi.colors.redHighlight
|
||||||
}
|
}
|
||||||
|
@ -446,9 +453,9 @@ Rectangle {
|
||||||
var selected = message.params[1];
|
var selected = message.params[1];
|
||||||
var userIndex = findSessionIndex(sessionIds[0]);
|
var userIndex = findSessionIndex(sessionIds[0]);
|
||||||
if (sessionIds.length > 1) {
|
if (sessionIds.length > 1) {
|
||||||
letterbox('Only one user can be selected at a time.');
|
letterbox("", "", 'Only one user can be selected at a time.');
|
||||||
} else if (userIndex < 0) {
|
} else if (userIndex < 0) {
|
||||||
letterbox('The last editor is not among this list of users.');
|
letterbox("", "", 'The last editor is not among this list of users.');
|
||||||
} else {
|
} else {
|
||||||
if (selected) {
|
if (selected) {
|
||||||
table.selection.clear(); // for now, no multi-select
|
table.selection.clear(); // for now, no multi-select
|
||||||
|
@ -465,6 +472,7 @@ Rectangle {
|
||||||
var userId = message.params[0];
|
var userId = message.params[0];
|
||||||
// The text that goes in the userName field is the second parameter in the message.
|
// The text that goes in the userName field is the second parameter in the message.
|
||||||
var userName = message.params[1];
|
var userName = message.params[1];
|
||||||
|
var admin = message.params[2];
|
||||||
// If the userId is empty, we're updating "myData".
|
// If the userId is empty, we're updating "myData".
|
||||||
if (!userId) {
|
if (!userId) {
|
||||||
myData.userName = userName;
|
myData.userName = userName;
|
||||||
|
@ -476,6 +484,9 @@ Rectangle {
|
||||||
// Set the userName appropriately
|
// Set the userName appropriately
|
||||||
userModel.setProperty(userIndex, "userName", userName);
|
userModel.setProperty(userIndex, "userName", userName);
|
||||||
userModelData[userIndex].userName = userName; // Defensive programming
|
userModelData[userIndex].userName = userName; // Defensive programming
|
||||||
|
// Set the admin status appropriately
|
||||||
|
userModel.setProperty(userIndex, "admin", admin);
|
||||||
|
userModelData[userIndex].admin = admin; // Defensive programming
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1040,25 +1040,20 @@ void NodeList::muteNodeBySessionID(const QUuid& nodeID) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeList::requestUsernameFromSessionID(const QUuid& nodeID) {
|
void NodeList::requestUsernameFromSessionID(const QUuid& nodeID) {
|
||||||
// send a request to domain-server to get the username associated with the given session ID
|
// send a request to domain-server to get the username/machine fingerprint/admin status associated with the given session ID
|
||||||
if (getThisNodeCanKick() || nodeID.isNull()) {
|
// If the requesting user isn't an admin, the username and machine fingerprint will return "".
|
||||||
// setup the packet
|
auto usernameFromIDRequestPacket = NLPacket::create(PacketType::UsernameFromIDRequest, NUM_BYTES_RFC4122_UUID, true);
|
||||||
auto usernameFromIDRequestPacket = NLPacket::create(PacketType::UsernameFromIDRequest, NUM_BYTES_RFC4122_UUID, true);
|
|
||||||
|
|
||||||
// write the node ID to the packet
|
// write the node ID to the packet
|
||||||
if (nodeID.isNull()) {
|
if (nodeID.isNull()) {
|
||||||
usernameFromIDRequestPacket->write(getSessionUUID().toRfc4122());
|
usernameFromIDRequestPacket->write(getSessionUUID().toRfc4122());
|
||||||
} else {
|
|
||||||
usernameFromIDRequestPacket->write(nodeID.toRfc4122());
|
|
||||||
}
|
|
||||||
|
|
||||||
qCDebug(networking) << "Sending packet to get username of node" << uuidStringWithoutCurlyBraces(nodeID);
|
|
||||||
|
|
||||||
sendPacket(std::move(usernameFromIDRequestPacket), _domainHandler.getSockAddr());
|
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "You do not have permissions to kick in this domain."
|
usernameFromIDRequestPacket->write(nodeID.toRfc4122());
|
||||||
<< "Request to get the username of node" << uuidStringWithoutCurlyBraces(nodeID) << "will not be sent";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qCDebug(networking) << "Sending packet to get username/fingerprint/admin status of node" << uuidStringWithoutCurlyBraces(nodeID);
|
||||||
|
|
||||||
|
sendPacket(std::move(usernameFromIDRequestPacket), _domainHandler.getSockAddr());
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeList::processUsernameFromIDReply(QSharedPointer<ReceivedMessage> message) {
|
void NodeList::processUsernameFromIDReply(QSharedPointer<ReceivedMessage> message) {
|
||||||
|
@ -1068,10 +1063,13 @@ void NodeList::processUsernameFromIDReply(QSharedPointer<ReceivedMessage> messag
|
||||||
QString username = message->readString();
|
QString username = message->readString();
|
||||||
// read the machine fingerprint from the packet
|
// read the machine fingerprint from the packet
|
||||||
QString machineFingerprintString = (QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID))).toString();
|
QString machineFingerprintString = (QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID))).toString();
|
||||||
|
bool isAdmin;
|
||||||
|
message->readPrimitive(&isAdmin);
|
||||||
|
|
||||||
qCDebug(networking) << "Got username" << username << "and machine fingerprint" << machineFingerprintString << "for node" << nodeUUIDString;
|
qCDebug(networking) << "Got username" << username << "and machine fingerprint"
|
||||||
|
<< machineFingerprintString << "for node" << nodeUUIDString << ". isAdmin:" << isAdmin;
|
||||||
|
|
||||||
emit usernameFromIDReply(nodeUUIDString, username, machineFingerprintString);
|
emit usernameFromIDReply(nodeUUIDString, username, machineFingerprintString, isAdmin);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NodeList::setRequestsDomainListData(bool isRequesting) {
|
void NodeList::setRequestsDomainListData(bool isRequesting) {
|
||||||
|
|
|
@ -120,7 +120,7 @@ signals:
|
||||||
void receivedDomainServerList();
|
void receivedDomainServerList();
|
||||||
void ignoredNode(const QUuid& nodeID, bool enabled);
|
void ignoredNode(const QUuid& nodeID, bool enabled);
|
||||||
void ignoreRadiusEnabledChanged(bool isIgnored);
|
void ignoreRadiusEnabledChanged(bool isIgnored);
|
||||||
void usernameFromIDReply(const QString& nodeID, const QString& username, const QString& machineFingerprint);
|
void usernameFromIDReply(const QString& nodeID, const QString& username, const QString& machineFingerprint, bool isAdmin);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void stopKeepalivePingTimer();
|
void stopKeepalivePingTimer();
|
||||||
|
|
|
@ -135,9 +135,10 @@ signals:
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Notifies scripts of the username and machine fingerprint associated with a UUID.
|
* Notifies scripts of the username and machine fingerprint associated with a UUID.
|
||||||
|
* Username and machineFingerprint will be their default constructor output if the requesting user isn't an admin.
|
||||||
* @function Users.usernameFromIDReply
|
* @function Users.usernameFromIDReply
|
||||||
*/
|
*/
|
||||||
void usernameFromIDReply(const QString& nodeID, const QString& username, const QString& machineFingerprint);
|
void usernameFromIDReply(const QString& nodeID, const QString& username, const QString& machineFingerprint, bool isAdmin);
|
||||||
|
|
||||||
/**jsdoc
|
/**jsdoc
|
||||||
* Notifies scripts that a user has disconnected from the domain
|
* Notifies scripts that a user has disconnected from the domain
|
||||||
|
|
|
@ -267,14 +267,12 @@ function populateUserList() {
|
||||||
displayName: avatar.sessionDisplayName,
|
displayName: avatar.sessionDisplayName,
|
||||||
userName: '',
|
userName: '',
|
||||||
sessionId: id || '',
|
sessionId: id || '',
|
||||||
audioLevel: 0.0
|
audioLevel: 0.0,
|
||||||
|
admin: false
|
||||||
};
|
};
|
||||||
// If the current user is an admin OR
|
// Request the username, fingerprint, and admin status from the given UUID
|
||||||
// they're requesting their own username ("id" is blank)...
|
// Username and fingerprint returns default constructor output if the requesting user isn't an admin
|
||||||
if (Users.canKick || !id) {
|
Users.requestUsernameFromID(id);
|
||||||
// Request the username from the given UUID
|
|
||||||
Users.requestUsernameFromID(id);
|
|
||||||
}
|
|
||||||
// Request personal mute status and ignore status
|
// Request personal mute status and ignore status
|
||||||
// from NodeList (as long as we're not requesting it for our own ID)
|
// from NodeList (as long as we're not requesting it for our own ID)
|
||||||
if (id) {
|
if (id) {
|
||||||
|
@ -289,16 +287,19 @@ function populateUserList() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// The function that handles the reply from the server
|
// The function that handles the reply from the server
|
||||||
function usernameFromIDReply(id, username, machineFingerprint) {
|
function usernameFromIDReply(id, username, machineFingerprint, isAdmin) {
|
||||||
var data;
|
var data;
|
||||||
// If the ID we've received is our ID...
|
// If the ID we've received is our ID...
|
||||||
if (MyAvatar.sessionUUID === id) {
|
if (MyAvatar.sessionUUID === id) {
|
||||||
// Set the data to contain specific strings.
|
// Set the data to contain specific strings.
|
||||||
data = ['', username];
|
data = ['', username, isAdmin];
|
||||||
} else {
|
} else if (Users.canKick) {
|
||||||
// Set the data to contain the ID and the username (if we have one)
|
// Set the data to contain the ID and the username (if we have one)
|
||||||
// or fingerprint (if we don't have a username) string.
|
// or fingerprint (if we don't have a username) string.
|
||||||
data = [id, username || machineFingerprint];
|
data = [id, username || machineFingerprint, isAdmin];
|
||||||
|
} else {
|
||||||
|
// Set the data to contain specific strings.
|
||||||
|
data = [id, '', isAdmin];
|
||||||
}
|
}
|
||||||
print('Username Data:', JSON.stringify(data));
|
print('Username Data:', JSON.stringify(data));
|
||||||
// Ship the data off to QML
|
// Ship the data off to QML
|
||||||
|
|
Loading…
Reference in a new issue