diff --git a/guacamole/src/main/webapp/admin.xhtml b/guacamole/src/main/webapp/admin.xhtml new file mode 100644 index 000000000..df6661b05 --- /dev/null +++ b/guacamole/src/main/webapp/admin.xhtml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + Guacamole ${project.version} + + + + +

Administration

+ +

Users

+
+ +

+ Click or tap on a user below to manage that user. Depending + on your access level, users can be added and deleted, and their + passwords can be changed. +

+ +
+
+
+ +
+ +

Connections

+
+ +

+ Click or tap on a connection below to manage that connection. + Depending on your access level, connections can be added and + deleted, and their properties (protocol, hostname, port, etc.) + can be changed. +

+ +
+
+
+ +
+
Another connection
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Protocol: + +
Hostname:
Password:
Port:
Read-only:
Swap red/blue:
+
+
+ + + +
+
+ +
+ +
+ Guacamole ${project.version} +
+ + + + + + + + + + + diff --git a/guacamole/src/main/webapp/images/action-icons/guac-config.png b/guacamole/src/main/webapp/images/action-icons/guac-config.png new file mode 100644 index 000000000..eb91fc0a0 Binary files /dev/null and b/guacamole/src/main/webapp/images/action-icons/guac-config.png differ diff --git a/guacamole/src/main/webapp/images/action-icons/guac-delete.png b/guacamole/src/main/webapp/images/action-icons/guac-delete.png new file mode 100644 index 000000000..925e95831 Binary files /dev/null and b/guacamole/src/main/webapp/images/action-icons/guac-delete.png differ diff --git a/guacamole/src/main/webapp/images/action-icons/guac-monitor-add.png b/guacamole/src/main/webapp/images/action-icons/guac-monitor-add.png new file mode 100644 index 000000000..0c9ea962c Binary files /dev/null and b/guacamole/src/main/webapp/images/action-icons/guac-monitor-add.png differ diff --git a/guacamole/src/main/webapp/images/action-icons/guac-user-add.png b/guacamole/src/main/webapp/images/action-icons/guac-user-add.png new file mode 100644 index 000000000..8b2d80ece Binary files /dev/null and b/guacamole/src/main/webapp/images/action-icons/guac-user-add.png differ diff --git a/guacamole/src/main/webapp/images/user-icons/guac-user.png b/guacamole/src/main/webapp/images/user-icons/guac-user.png new file mode 100644 index 000000000..527004040 Binary files /dev/null and b/guacamole/src/main/webapp/images/user-icons/guac-user.png differ diff --git a/guacamole/src/main/webapp/scripts/guac-ui.js b/guacamole/src/main/webapp/scripts/guac-ui.js index f543a86ee..a84e8a956 100644 --- a/guacamole/src/main/webapp/scripts/guac-ui.js +++ b/guacamole/src/main/webapp/scripts/guac-ui.js @@ -392,4 +392,245 @@ GuacUI.DraggableComponent = function(element) { */ this.onmove = null; -}; \ No newline at end of file +}; + +/** + * A connection UI object which can be easily added to a list of connections + * for sake of display. + */ +GuacUI.Connection = function(connection) { + + /** + * The actual connection associated with this connection UI element. + */ + this.connection = connection; + + function createElement(tagname, classname) { + var new_element = document.createElement(tagname); + new_element.className = classname; + return new_element; + } + + // Create connection display elements + var element = createElement("div", "connection"); + var caption = createElement("div", "caption"); + var protocol = createElement("div", "protocol"); + var name = createElement("span", "name"); + var protocol_icon = createElement("div", "icon " + connection.protocol); + var thumbnail = createElement("div", "thumbnail"); + var thumb_img; + + // Get URL + var url = "client.xhtml?id=" + encodeURIComponent(connection.id); + + // Create link to client + element.onclick = function() { + + // Attempt to focus existing window + var current = window.open(null, connection.id); + + // If window did not already exist, set up as + // Guacamole client + if (!current.GuacUI) + window.open(url, connection.id); + + }; + + // Add icon + protocol.appendChild(protocol_icon); + + // Set name + name.textContent = connection.id; + + // Assemble caption + caption.appendChild(protocol); + caption.appendChild(name); + + // Assemble connection icon + element.appendChild(thumbnail); + element.appendChild(caption); + + // Add screenshot if available + var thumbnail_url = GuacamoleHistory.get(connection.id).thumbnail; + if (thumbnail_url) { + + // Create thumbnail element + thumb_img = document.createElement("img"); + thumb_img.src = thumbnail_url; + thumbnail.appendChild(thumb_img); + + } + + /** + * Returns the DOM element representing this connection. + */ + this.getElement = function() { + return element; + }; + + /** + * Returns whether this connection has an associated thumbnail. + */ + this.hasThumbnail = function() { + return thumb_img && true; + }; + + /** + * Sets the thumbnail URL of this existing connection. Note that this will + * only work if the connection already had a thumbnail associated with it. + */ + this.setThumbnail = function(url) { + + // If no image element, create it + if (!thumb_img) { + thumb_img = document.createElement("img"); + thumb_img.src = url; + thumbnail.appendChild(thumb_img); + } + + // Otherwise, set source of existing + else + thumb_img.src = url; + + }; + +}; + +/** + * An arbitrary table-based form. + */ +GuacUI.Form = function() { + + var element = GuacUI.createElement("table"); + + /** + * Returns the DOM element representing this form. + */ + this.getElement = function() { + return element; + }; + + this.addField = function(title, type, value) { + + // Add elements + var row = GuacUI.createChildElement(element, "tr"); + var header = GuacUI.createChildElement(row, "th"); + var cell = GuacUI.createChildElement(row, "td"); + var input = GuacUI.createChildElement(cell, "input"); + + // Set title + header.textContent = title; + + // Set type and value + input.setAttribute("type", type); + if (value) input.setAttribute("value", value); + + }; + +}; + +/** + * A user UI object. + */ +GuacUI.User = function(username) { + + /** + * This user's username. + */ + this.username = username; + + var user_type = "normal"; + + // Create connection display elements + var element = GuacUI.createElement("div", "user"); + var caption = GuacUI.createChildElement(element ,"div", "caption"); + var type = GuacUI.createChildElement(caption, "div", "type"); + var name = GuacUI.createChildElement(caption, "span", "name"); + GuacUI.createChildElement(type, "div", "icon " + user_type); + + // Get URL + var url = "user.xhtml?name=" + encodeURIComponent(username); + + // Create link to client + element.onclick = function() { + + // Attempt to focus existing window + var current = window.open(null, username); + + // If window did not already exist, set up as + // Guacamole client + if (!current.GuacUI) + window.open(url, username); + + }; + + // Set name + name.textContent = username; + + /** + * Returns the DOM element representing this connection. + */ + this.getElement = function() { + return element; + }; + +}; + + +/** + * An editable user UI object. + */ +GuacUI.EditableUser = function(username) { + + /** + * This user's username. + */ + this.username = username; + + /** + * Current status of edit mode. + */ + this.edit = false; + + // Create contained user + var user = new GuacUI.User(username); + var element = user.getElement(); + GuacUI.addClass(element, "editable"); + + // Fields + var fields = GuacUI.createChildElement(element, "div", "fields"); + var form = new GuacUI.Form(); + + // Add form + fields.appendChild(form.getElement()); + + // Add fields + form.addField("Password:", "password", "123412341234"); + form.addField("Re-enter Password:", "password", "123412341234"); + + /** + * Returns the DOM element representing this connection. + */ + this.getElement = function() { + return element; + }; + + /** + * Sets/unsets edit mode. When edit mode is on, the user's properties + * will be visible and editable. + */ + this.setEditMode = function(enabled) { + + // Set edit mode + this.edit = enabled; + + // Alter class accordingly + if (enabled) + GuacUI.addClass(element, "edit"); + else + GuacUI.removeClass(element, "edit"); + + }; + +}; + diff --git a/guacamole/src/main/webapp/scripts/root-ui.js b/guacamole/src/main/webapp/scripts/root-ui.js index 5beb8cd90..51ba19e27 100644 --- a/guacamole/src/main/webapp/scripts/root-ui.js +++ b/guacamole/src/main/webapp/scripts/root-ui.js @@ -91,108 +91,6 @@ GuacamoleRootUI.login = function(username, password) { }; -/** - * A connection UI object which can be easily added to a list of connections - * for sake of display. - */ -GuacamoleRootUI.Connection = function(connection) { - - /** - * The actual connection associated with this connection UI element. - */ - this.connection = connection; - - function createElement(tagname, classname) { - var new_element = document.createElement(tagname); - new_element.className = classname; - return new_element; - } - - // Create connection display elements - var element = createElement("div", "connection"); - var caption = createElement("div", "caption"); - var protocol = createElement("div", "protocol"); - var name = createElement("span", "name"); - var protocol_icon = createElement("div", "icon " + connection.protocol); - var thumbnail = createElement("div", "thumbnail"); - var thumb_img; - - // Get URL - var url = "client.xhtml?id=" + encodeURIComponent(connection.id); - - // Create link to client - element.onclick = function() { - - // Attempt to focus existing window - var current = window.open(null, connection.id); - - // If window did not already exist, set up as - // Guacamole client - if (!current.GuacUI) - window.open(url, connection.id); - - }; - - // Add icon - protocol.appendChild(protocol_icon); - - // Set name - name.textContent = connection.id; - - // Assemble caption - caption.appendChild(protocol); - caption.appendChild(name); - - // Assemble connection icon - element.appendChild(thumbnail); - element.appendChild(caption); - - // Add screenshot if available - var thumbnail_url = GuacamoleHistory.get(connection.id).thumbnail; - if (thumbnail_url) { - - // Create thumbnail element - thumb_img = document.createElement("img"); - thumb_img.src = thumbnail_url; - thumbnail.appendChild(thumb_img); - - } - - /** - * Returns the DOM element representing this connection. - */ - this.getElement = function() { - return element; - }; - - /** - * Returns whether this connection has an associated thumbnail. - */ - this.hasThumbnail = function() { - return thumb_img && true; - }; - - /** - * Sets the thumbnail URL of this existing connection. Note that this will - * only work if the connection already had a thumbnail associated with it. - */ - this.setThumbnail = function(url) { - - // If no image element, create it - if (!thumb_img) { - thumb_img = document.createElement("img"); - thumb_img.src = url; - thumbnail.appendChild(thumb_img); - } - - // Otherwise, set source of existing - else - thumb_img.src = url; - - }; - -}; - /** * Set of all thumbnailed connections, indexed by ID. */ diff --git a/guacamole/src/main/webapp/styles/animation.css b/guacamole/src/main/webapp/styles/animation.css new file mode 100644 index 000000000..80bb237a4 --- /dev/null +++ b/guacamole/src/main/webapp/styles/animation.css @@ -0,0 +1,35 @@ + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +/** + * fadein: Fade from fully transparent to fully opaque. + */ +@keyframes fadein { + from { opacity: 0; } + to { opacity: 1; } +} +@-moz-keyframes fadein { + from { opacity: 0; } + to { opacity: 1; } +} +@-webkit-keyframes fadein { + from { opacity: 0; } + to { opacity: 1; } +} + diff --git a/guacamole/src/main/webapp/styles/ui.css b/guacamole/src/main/webapp/styles/ui.css new file mode 100644 index 000000000..f7e99f23c --- /dev/null +++ b/guacamole/src/main/webapp/styles/ui.css @@ -0,0 +1,328 @@ + +/* + * Guacamole - Clientless Remote Desktop + * Copyright (C) 2010 Michael Jumper + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +@import url('animation.css'); + +* { + -webkit-tap-highlight-color: rgba(0,0,0,0); +} + +input[type=checkbox], input[type=text], textarea { + -webkit-tap-highlight-color: rgba(128,192,128,0.5); +} + +input[type=submit], button { + -webkit-appearance: none; +} + +input[type=text], input[type=password] { + border: 1px solid #777; + -moz-border-radius: 0.2em; + -webkit-border-radius: 0.2em; + -khtml-border-radius: 0.2em; + border-radius: 0.2em; + width: 100%; +} + +button { + + background: #8A6; + border: 1px solid rgba(0, 0, 0, 0.4); + -moz-border-radius: 0.6em; + -webkit-border-radius: 0.6em; + -khtml-border-radius: 0.6em; + border-radius: 0.6em; + + color: white; + text-shadow: -1px -1px rgba(0, 0, 0, 0.3); + font-weight: bold; + font-size: 1.125em; + + box-shadow: inset -1px -1px 0.25em rgba(0, 0, 0, 0.25), + inset 1px 1px 0.25em rgba(255, 255, 255, 0.25), + -1px -1px 0.25em rgba(0, 0, 0, 0.25), + 1px 1px 0.25em rgba(255, 255, 255, 0.25); + + padding: 0.35em; + padding-right: 1em; + padding-left: 1em; + min-width: 5em; + +} + +button:hover { + background: #9C7; +} + +button:active { + + padding-left: 1.1em; + padding-right: 0.9em; + padding-top: 0.45em; + padding-bottom: 0.25em; + + box-shadow: + inset 1px 1px 0.25em rgba(0, 0, 0, 0.25), + -1px -1px 0.25em rgba(0, 0, 0, 0.25), + 1px 1px 0.25em rgba(255, 255, 255, 0.25); +} + +button.danger { + background: #A43; +} + +button.danger:hover { + background: #C54; +} + +body { + background: #EEE; + font-family: FreeSans, Helvetica, Arial, sans-serif; + padding: 0; + margin: 0; +} + +img { + border: none; + vertical-align: middle; +} + +div#version-dialog { + position: fixed; + right: 0; + bottom: 0; + text-align: right; + + font-style: italic; + font-size: 0.75em; + color: black; + opacity: 0.5; + + padding: 0.5em; +} + +h2 { + + padding: 0.5em; + margin: 0; + font-size: 1.5em; + + font-weight: lighter; + text-shadow: 1px 1px white; + + border-top: 1px solid #AAA; + border-bottom: 1px solid #AAA; + background: #DDD; + +} + +h1 { + + margin: 0; + padding: 0.5em; + + font-size: 2em; + vertical-align: middle; + text-align: center; + +} + +div.section { + margin: 0; + padding: 1em; +} + +/* + * List elements + */ + +.user, +.connection { + + display: block; + text-align: left; + padding: 0.1em; + cursor: pointer; + + -moz-border-radius: 0.2em; + -webkit-border-radius: 0.2em; + -khtml-border-radius: 0.2em; + border-radius: 0.2em; + border: 1px solid rgba(0, 0, 0, 0); + + position: relative; + +} + +.connection .thumbnail { + margin: 0.5em; + display: none; +} + +.connection .thumbnail img { + border: 1px solid black; + box-shadow: 1px 1px 5px black; + max-width: 75%; +} + +.user .type, +.connection .protocol { + display: inline-block; +} + +.user .icon, +.connection .icon { + width: 24px; + height: 24px; + background-size: 16px 16px; + -moz-background-size: 16px 16px; + -webkit-background-size: 16px 16px; + -khtml-background-size: 16px 16px; + background-repeat: no-repeat; + background-position: center center; + opacity: 0.5; +} + +.user .caption, +.connection .caption { + display: inline-block; + vertical-align: top; +} + +.user .caption *, +.connection .caption * { + vertical-align: middle; +} + +.user .name, +.connection .name { + color: black; + font-weight: normal; + padding: 0.1em; + margin-left: 0.25em; +} + +/* + * List element edit / non-edit styling + */ + +.user.edit, +.connection.edit { + background: #DEB; + border: 1px solid rgba(0, 0, 0, 0.25); +} + +.user.edit .icon, +.connection.edit .icon { + opacity: 1.0; +} + +.user:not(.edit):hover, +.connection:not(.edit):hover { + background: #CDA; +} + +/* + * List element fields (editing) + */ + +.user .fields, +.connection .fields { + + position: absolute; + display: inline-block; + vertical-align: top; + z-index: 1; + + border: 1px solid rgba(0, 0, 0, 0.1); + background: #DDD; + padding: 0.25em; + margin: 0.25em; + + -moz-border-radius: 0.2em; + -webkit-border-radius: 0.2em; + -khtml-border-radius: 0.2em; + border-radius: 0.2em; + + box-shadow: 0.1em 0.1em 0.2em rgba(0, 0, 0, 0.6); + +} + +.user .fields th, +.connection .fields th { + font-weight: normal; + font-size: 0.8em; +} + +.user:not(.edit) .fields, +.connection:not(.edit) .fields { + display: none; +} + +.user.edit .fields, +.connection.edit .fields { + animation-name: fadein; + -webkit-animation-name: fadein; + animation-duration: 0.125s; + -webkit-animation-duration: 0.125s; +} + +/* + * List element icons + */ + +.user .icon { + background-image: url('../images/user-icons/guac-user.png'); +} + +.user .icon.add { + background-image: url('../images/action-icons/guac-user-add.png'); +} + +.protocol .icon { + background-image: url('../images/protocol-icons/guac-plug.png'); +} + +.protocol .icon.ssh { + background-image: url('../images/protocol-icons/guac-text.png'); +} + +.protocol .icon.add{ + background-image: url('../images/action-icons/guac-monitor-add.png'); +} + +.protocol .icon.vnc, +.protocol .icon.rdp { + background-image: url('../images/protocol-icons/guac-monitor.png'); +} + +/* + * Settings formatting + */ + +.settings dt { + border-bottom: 1px dotted #AAA; + padding-bottom: 0.25em; +} + +.settings dd { + margin: 1.5em; + margin-left: 2.5em; + font-size: 0.75em; +}