Commit 340f5023 authored by Nico Eckes's avatar Nico Eckes

Finished PermIO Editor

parent 850ec4a3
Pipeline #791 passed with stage
in 4 minutes and 28 seconds
......@@ -26,7 +26,9 @@ configurations {
dependencies {
embed group: 'org.yaml', name: 'snakeyaml', version: '1.17'
compile 'org.jetbrains:annotations:13.0'
embed group: 'org.apache.commons', name: 'commons-vfs2-project', version: '2.2'
embed group: 'commons-logging', name: 'commons-logging', version: '1.2'
compile 'org.jetbrains:annotations:13.0'
}
jar {
......
......@@ -8,15 +8,17 @@ import javafx.scene.image.Image;
import javafx.stage.Stage;
public class Main extends Application {
public static final Image ICON = new Image(Main.class.getResourceAsStream("img/PermIO.png"));
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("MainScreen.fxml"));
Parent root = FXMLLoader.load(getClass().getResource("screen/MainScreen.fxml"));
primaryStage.setTitle("Permission Editor");
primaryStage.getIcons().add(new Image(getClass().getClassLoader().getResourceAsStream("com/unitedworldminers/permio/editor/img/PermIO.png")));
primaryStage.getIcons().add(ICON);
primaryStage.setScene(new Scene(root, 1000, 500));
primaryStage.show();
}
......
package com.unitedworldminers.permio.editor;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.control.Menu;
import javafx.scene.control.MenuBar;
import javafx.scene.control.MenuItem;
import org.jetbrains.annotations.Nullable;
import java.nio.file.Path;
import java.nio.file.Paths;
public class MainScreen {
@FXML private Node groupsScreen;
@FXML private MenuBar menuBar;
public static GroupData groups = new GroupData();
public static PlayerData players = new PlayerData();
@FXML
private void initialize() {
// menubar direct item click hack
for (Menu m: menuBar.getMenus()) {
m.getItems().add(new MenuItem());
m.addEventHandler(Menu.ON_SHOWN, event -> {
m.hide();
m.fire();
});
}
// load if in fitting dir
boolean loaded = load(Paths.get("groups.yml"), Paths.get("players.yml")) != null || load(Paths.get("../groups.yml"), Paths.get("../players.yml")) != null;
System.out.println(loaded ? "loaded" : "empty");
}
@FXML
private void menuOpen(ActionEvent actionEvent) {
}
@FXML
private void menuSave(ActionEvent actionEvent) {
System.out.println();
}
@FXML
private void menuReload(ActionEvent actionEvent) {
load(groups.location, players.location);
}
@Nullable
private String load(Path groupsFile, Path playersFile) {
try {
groups = new GroupData(groupsFile);
players = new PlayerData(playersFile, groups);
} catch (InstantiationException e) {
return e.getMessage();
}
((GroupsScreen) groupsScreen.getUserData()).load();
return null;
}
}
package com.unitedworldminers.permio.editor;
package com.unitedworldminers.permio.editor.component;
import javafx.application.Platform;
import javafx.beans.property.ObjectProperty;
......@@ -17,6 +17,7 @@ import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
......@@ -31,9 +32,11 @@ import java.util.stream.Collectors;
public abstract class ExtendedList<T> extends VBox {
@FXML public Label title;
@FXML public HBox header;
@FXML public TextField searchField;
@FXML public TextField addField;
@FXML public ListView<T> list;
@FXML public HBox toolbar;
private ObservableList<T> items;
public final ObjectProperty<T> selectedItem = new SimpleObjectProperty<>();
......@@ -85,7 +88,7 @@ public abstract class ExtendedList<T> extends VBox {
addField.textProperty().addListener(obs -> addField.setStyle(null));
getChildren().addAll(title, searchField, list, addField);
getChildren().addAll(header, searchField, list, addField);
}
public void setSource(ObservableList<T> source) {
......@@ -144,7 +147,7 @@ public abstract class ExtendedList<T> extends VBox {
setMinWidth(0);
setPrefWidth(1);
ImageView removeBtn = new ImageView(new Image(getClass().getResourceAsStream("img/remove.png")));
ImageView removeBtn = new ImageView(new Image(getClass().getResourceAsStream("../img/remove.png")));
removeBtn.setOnMouseClicked(event -> items.remove(item));
Node node = provideNode(item);
setGraphic(new StackPane(node, removeBtn));
......
package com.unitedworldminers.permio.editor;
package com.unitedworldminers.permio.editor.component;
import com.unitedworldminers.permio.editor.util.Utils;
import com.unitedworldminers.permio.editor.data.Group;
import javafx.scene.Node;
import javafx.scene.control.Label;
......
package com.unitedworldminers.permio.editor;
package com.unitedworldminers.permio.editor.component;
import javafx.scene.Node;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.util.Pair;
import org.jetbrains.annotations.NotNull;
import java.util.Comparator;
......@@ -16,6 +17,25 @@ public class PermissionList extends ExtendedList<String> {
@Override
public void initialize() {
super.initialize();
ImageView paletteReload = new ImageView(new Image(getClass().getResourceAsStream("../img/colorpalette.png"), 16, 16, true, false));
paletteReload.setOnMouseClicked(event -> {
PermissionLabel.reloadColors();
list.refresh();
});
Image hidden = new Image(getClass().getResourceAsStream("../img/allcmdshidden.png"), -1, 16, true, false);
Image shown = new Image(getClass().getResourceAsStream("../img/allcmdsshown.png"), -1, 16, true, false);
ImageView showHidden = new ImageView(hidden);
showHidden.setOnMouseClicked(event -> {
if (showHidden.getImage() == hidden) {
showHidden.setImage(shown);
} else {
showHidden.setImage(hidden);
}
});
//TODO add shown button
toolbar.getChildren().addAll(paletteReload);
title.setText("Permissions");
addField.setPromptText("New Permission...");
}
......@@ -36,13 +56,17 @@ public class PermissionList extends ExtendedList<String> {
}
private static class PermissionLabel extends TextFlow {
public static void reloadColors() {
colors.recolorAll();
}
private static final Random random = new Random();
private static final ColorMap colors = new ColorMap();
private final String text;
public PermissionLabel(String text) {
public PermissionLabel(@NotNull String text) {
this.text = text;
final ColorMap[] map = new ColorMap[]{colors};
String[] parts = text.split("\\.");
......@@ -51,9 +75,9 @@ public class PermissionList extends ExtendedList<String> {
} else {
getChildren().addAll(Stream.of(parts).flatMap(s -> {
Text t = new Text(s);
Pair<Color, ColorMap> data = map[0].computeIfAbsent(s, s1 -> new Pair<>(nextColor(), new ColorMap()));
map[0] = data.getValue();
t.setFill(data.getKey());
ColorMapEntry data = map[0].computeIfAbsent(s, s1 -> new ColorMapEntry());
map[0] = data.map;
t.setFill(data.color);
return Stream.of(t, new Text("."));
}).limit(parts.length * 2 - 1).toArray(Text[]::new));
}
......@@ -64,13 +88,23 @@ public class PermissionList extends ExtendedList<String> {
return text;
}
@NotNull
private static Color nextColor() {
return Color.rgb(random.nextInt(256), random.nextInt(256), random.nextInt(256));
private static class ColorMap extends HashMap<String, ColorMapEntry> {
public void recolorAll() {
for (ColorMapEntry e: values()) {
e.color = nextColor();
e.map.recolorAll();
}
}
}
private static class ColorMap extends HashMap<String, Pair<Color, ColorMap>> {
private static class ColorMapEntry {
public Color color = nextColor();
public ColorMap map = new ColorMap();
}
@NotNull
private static Color nextColor() {
return Color.rgb(random.nextInt(256), random.nextInt(256), random.nextInt(256));
}
}
}
package com.unitedworldminers.permio.editor;
package com.unitedworldminers.permio.editor.component;
import com.unitedworldminers.permio.editor.data.Player;
import com.unitedworldminers.permio.editor.data.PlayerProfile;
import com.unitedworldminers.permio.editor.util.Utils;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.Label;
......
package com.unitedworldminers.permio.editor;
package com.unitedworldminers.permio.editor.data;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
......@@ -45,4 +46,13 @@ public class Group {
public boolean isChildOf(Group parent) {
return this == parent || (inherits.get() != null && inherits.get().isChildOf(parent));
}
public Map<String, Object> save() {
Map<String, Object> result = new HashMap<>();
result.put(KEY_PERMISSIONS, permissions);
result.put(KEY_INHERITS, inherits.get());
result.put(KEY_PREFIX, prefix.get());
result.put(KEY_SUFFIX, suffix.get());
return result;
}
}
package com.unitedworldminers.permio.editor;
package com.unitedworldminers.permio.editor.data;
import com.unitedworldminers.permio.editor.exception.LoadException;
import com.unitedworldminers.permio.editor.util.YamlUtil;
import javafx.beans.InvalidationListener;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import org.apache.commons.vfs2.FileContent;
import org.apache.commons.vfs2.FileSystemException;
import org.jetbrains.annotations.NotNull;
import java.nio.file.Path;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class GroupData implements ObservableList<Group> {
public Path location = null;
public FileContent file;
private ObservableList<Group> groups;
public GroupData() {
groups = FXCollections.observableArrayList();
}
public GroupData(Path groupsFile) throws InstantiationException {
location = groupsFile;
public GroupData(FileContent file) throws LoadException {
this.file = file;
groups = FXCollections.observableArrayList();
Map<String, Map<String, Object>> rawGroups = YamlUtil.loadYaml(groupsFile);
if (rawGroups == null) throw new InstantiationException("Could not load groups from "+groupsFile.toString());
Map<String, Map<String, Object>> rawGroups;
try {
rawGroups = YamlUtil.loadYaml(file);
} catch (FileSystemException e) {
throw new LoadException("Could not load groups from " + file.getFile().getPublicURIString(), e);
}
if (rawGroups == null) throw new LoadException("Could not load groups from " + file.getFile().getPublicURIString());
rawGroups.keySet().forEach(gid -> provideGroup(gid, rawGroups));
}
......@@ -47,6 +56,14 @@ public class GroupData implements ObservableList<Group> {
return null;
}
public void save() {
try {
YamlUtil.saveYaml(groups.stream().collect(Collectors.toMap(g->g.name, Group::save)), file);
} catch (FileSystemException e) {
e.printStackTrace();
}
}
// delegated methods
public void addListener(ListChangeListener<? super Group> listener) {
......
package com.unitedworldminers.permio.editor;
package com.unitedworldminers.permio.editor.data;
import javafx.beans.property.*;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
......@@ -40,4 +44,13 @@ public class Player {
public String toString() {
return identity.toString();
}
public Map<String, Object> save() {
Map<String, Object> result = new HashMap<>();
result.put(KEY_PERMISSIONS, permissions);
result.put(KEY_GROUP, group.get());
result.put(KEY_PREFIX, prefix.get());
result.put(KEY_SUFFIX, suffix.get());
return result;
}
}
package com.unitedworldminers.permio.editor;
package com.unitedworldminers.permio.editor.data;
import com.unitedworldminers.permio.editor.exception.LoadException;
import com.unitedworldminers.permio.editor.util.YamlUtil;
import javafx.beans.InvalidationListener;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import org.apache.commons.vfs2.FileContent;
import org.apache.commons.vfs2.FileSystemException;
import org.jetbrains.annotations.NotNull;
import java.nio.file.Path;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
......@@ -17,17 +20,22 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
public class PlayerData implements ObservableList<Player> {
public Path location = null;
public FileContent file;
public ObservableList<Player> players;
public PlayerData() {
players = FXCollections.observableArrayList();
}
public PlayerData(Path playersFile, GroupData groupData) throws InstantiationException {
location = playersFile;
Map<String, Map<String, Object>> rawPlayers = YamlUtil.loadYaml(playersFile);
if (rawPlayers == null) throw new InstantiationException("Could not load players from "+playersFile.toString());
public PlayerData(FileContent file, GroupData groupData) throws LoadException {
this.file = file;
Map<String, Map<String, Object>> rawPlayers;
try {
rawPlayers = YamlUtil.loadYaml(file);
} catch (FileSystemException e) {
throw new LoadException("Could not load players from " + file.getFile().getPublicURIString());
}
if (rawPlayers == null) throw new LoadException("Could not load players from " + file.getFile().getPublicURIString());
players = FXCollections.observableArrayList(rawPlayers.entrySet().stream().map(e->new Player(PlayerProfile.query(e.getKey())[0], e.getValue(), groupData)).collect(Collectors.toList()));
}
......@@ -36,6 +44,14 @@ public class PlayerData implements ObservableList<Player> {
return null;
}
public void save() {
try {
YamlUtil.saveYaml(players.stream().collect(Collectors.toMap(p->p.identity.uuid.toString(), Player::save)), file);
} catch (FileSystemException e) {
e.printStackTrace();
}
}
public void addListener(ListChangeListener<? super Player> listener) {
players.addListener(listener);
}
......
package com.unitedworldminers.permio.editor;
package com.unitedworldminers.permio.editor.data;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
......@@ -68,8 +68,11 @@ public class PlayerProfile {
cache.put(uuid.toString(), this);
new Thread(()->{
name.set(getName(uuid));
cache.put(name.get().toLowerCase(), this);
String name = getName(uuid);
if (name != null) {
this.name.set(name);
cache.put(name.toLowerCase(), this);
}
}).start();
}
......
package com.unitedworldminers.permio.editor.exception;
public class LoadException extends Exception {
public LoadException() {
super();
}
public LoadException(String message) {
super(message);
}
public LoadException(String message, Throwable cause) {
super(message, cause);
}
public LoadException(Throwable cause) {
super(cause);
}
}
package com.unitedworldminers.permio.editor;
package com.unitedworldminers.permio.editor.screen;
import com.unitedworldminers.permio.editor.data.Group;
import com.unitedworldminers.permio.editor.component.GroupsList;
import com.unitedworldminers.permio.editor.component.PermissionList;
import com.unitedworldminers.permio.editor.component.PlayerList;
import javafx.fxml.FXML;
import javafx.scene.control.ComboBox;
import javafx.scene.control.TextField;
......@@ -36,7 +40,10 @@ public class GroupsScreen {
setElementsDisabled(now == null);
if (now != null) {
permissions.setSource(now.permissions);
if (old != null) inheritance.valueProperty().unbindBidirectional(old.inherits);
inheritance.setItems(MainScreen.groups.filtered(g -> !g.isChildOf(now)));
inheritance.valueProperty().bindBidirectional(now.inherits);
if (old != null) prefix.textProperty().unbindBidirectional(old.prefix);
prefix.textProperty().bindBidirectional(now.prefix);
......
package com.unitedworldminers.permio.editor.screen;
import com.unitedworldminers.permio.editor.Main;
import com.unitedworldminers.permio.editor.data.GroupData;
import com.unitedworldminers.permio.editor.data.PlayerData;
import com.unitedworldminers.permio.editor.exception.LoadException;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.MenuBar;
import javafx.stage.FileChooser;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.Window;
import javafx.util.Pair;
import org.apache.commons.vfs2.FileContent;
import org.apache.commons.vfs2.FileSystemManager;
import org.apache.commons.vfs2.VFS;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Optional;
public class MainScreen {
@FXML private Node groupsScreen;
@FXML private MenuBar menuBar;
public static GroupData groups = new GroupData();
public static PlayerData players = new PlayerData();
@FXML
private void initialize() {
// load if in fitting dir
boolean loaded = load(Paths.get("groups.yml"), Paths.get("players.yml")) != null || load(Paths.get("../groups.yml"), Paths.get("../players.yml")) != null;
System.out.println(loaded ? "loaded" : "empty");
}
@FXML
private void menuOpen(ActionEvent actionEvent) {
Window window = menuBar.getScene().getWindow();
FileChooser chooser = new FileChooser();
chooser.setSelectedExtensionFilter(new FileChooser.ExtensionFilter("YAML file", "*.yml"));
chooser.setInitialDirectory(new File("."));
chooser.setTitle("Select groups file");
chooser.setInitialFileName("groups.yml");
Path groups = Optional.ofNullable(chooser.showOpenDialog(window)).map(File::toPath).orElse(null);
if (groups == null) return;
chooser.setTitle("Select players file");
chooser.setInitialFileName("players.yml");
Path players = Optional.ofNullable(chooser.showOpenDialog(window)).map(File::toPath).orElse(null);
if (players == null) return;
String err = load(groups, players);
if (err != null) {
Alert dialog = new Alert(Alert.AlertType.ERROR);
dialog.setContentText("Error loading data: " + err);
dialog.showAndWait();
}
}
@FXML
private void menuSave(ActionEvent actionEvent) {
save();
}
@FXML
private void menuReload(ActionEvent actionEvent) {
load(groups.file, players.file);
}
@Nullable
private String load(Path groupsFile, Path playersFile) {
try {
FileSystemManager fs = VFS.getManager();
return load(fs.resolveFile(groupsFile.toUri()).getContent(), fs.resolveFile(playersFile.toUri()).getContent());
} catch (IOException e) {
return e.getMessage();
}
}
@Nullable
private String load(FileContent groups, FileContent players) {
try {
MainScreen.groups = new GroupData(groups);
MainScreen.players = new PlayerData(players, MainScreen.groups);
} catch (LoadException e) {
return e.getMessage();
}
((GroupsScreen) groupsScreen.getUserData()).load();
return null;
}
@Nullable
private String save() {
try {
groups.save();
players.save();
} catch (Exception e) {
return e.getMessage();
}
return null;
}
public void menuOpenSFTP() {
Stage window = new Stage();
window.initOwner(menuBar.getScene().getWindow());
window.initModality(Modality.WINDOW_MODAL);
window.setTitle("Connect to server");
window.getIcons().add(Main.ICON);
try {
window.setScene(new Scene(FXMLLoader.load(getClass().getResource("SftpDialog.fxml"))));
} catch (IOException e) {
e.printStackTrace();
}
window.showAndWait();