ported legacy functions
This commit is contained in:
@@ -3,7 +3,6 @@ package de.srsoftware.umbrella.backend;
|
||||
|
||||
import static de.srsoftware.umbrella.core.Constants.UMBRELLA;
|
||||
import static de.srsoftware.umbrella.core.Util.mapLogLevel;
|
||||
import static java.lang.System.Logger.Level.DEBUG;
|
||||
import static java.lang.System.Logger.Level.INFO;
|
||||
|
||||
import com.sun.net.httpserver.HttpServer;
|
||||
@@ -16,11 +15,10 @@ import de.srsoftware.umbrella.translations.Translations;
|
||||
import de.srsoftware.umbrella.user.UserModule;
|
||||
import de.srsoftware.umbrella.user.sqlite.SqliteDB;
|
||||
import de.srsoftware.umbrella.web.WebHandler;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.concurrent.Executors;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class Application {
|
||||
private static final System.Logger LOG = System.getLogger("Umbrella");
|
||||
|
||||
@@ -1,19 +1,18 @@
|
||||
/* © SRSoftware 2025 */
|
||||
package de.srsoftware.umbrella.core;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import de.srsoftware.tools.Path;
|
||||
import de.srsoftware.tools.PathHandler;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import static de.srsoftware.tools.MimeType.MIME_JSON;
|
||||
import static de.srsoftware.tools.Optionals.nullable;
|
||||
import static java.lang.System.Logger.Level.DEBUG;
|
||||
import static java.lang.System.Logger.Level.WARNING;
|
||||
import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import de.srsoftware.tools.Path;
|
||||
import de.srsoftware.tools.PathHandler;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class BaseHandler extends PathHandler {
|
||||
|
||||
private static HttpExchange addCors(HttpExchange ex){
|
||||
|
||||
@@ -6,6 +6,7 @@ public class Paths {
|
||||
|
||||
public static final String ADD = "add";
|
||||
public static final String CSS = "css";
|
||||
public static final String COMMON_TEMPLATES = "common_templates";
|
||||
public static final String JSON = "json";
|
||||
public static final String LEGACY = "legacy";
|
||||
public static final String LIST = "list";
|
||||
|
||||
@@ -4,7 +4,6 @@ package de.srsoftware.umbrella.core;
|
||||
import static de.srsoftware.umbrella.core.Constants.TOKEN;
|
||||
|
||||
import de.srsoftware.tools.SessionToken;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class Token implements CharSequence{
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
<!-- https://github.com/notnotsamuel/svelte-tiny-router -->
|
||||
<Menu />
|
||||
<Route path="/user" component={User} />
|
||||
<Route path="/user/login" component={User} />
|
||||
<Route path="/user/:user_id/edit" component={UserEdit} />
|
||||
<Route path="/user/oidc/add" component={EditService} />
|
||||
<Route path="/user/oidc/edit/:serviceName" component={EditService} />
|
||||
|
||||
@@ -6,6 +6,12 @@
|
||||
import LoginServiceList from './LoginServices.svelte';
|
||||
import Profile from './Profile.svelte';
|
||||
import UserList from './List.svelte';
|
||||
|
||||
let params = new URLSearchParams(location.search);
|
||||
let redirect = params.get('returnTo');
|
||||
if (redirect && user.name){
|
||||
location.href = redirect;
|
||||
}
|
||||
</script>
|
||||
|
||||
<h1>{t('user.user_module')}</h1>
|
||||
|
||||
14
legacy/build.gradle.kts
Normal file
14
legacy/build.gradle.kts
Normal file
@@ -0,0 +1,14 @@
|
||||
description = "Umbrella : Legacy API"
|
||||
|
||||
dependencies{
|
||||
implementation(project(":core"))
|
||||
implementation(project(":user"))
|
||||
implementation("de.srsoftware:configuration.api:1.0.2")
|
||||
implementation("de.srsoftware:tools.jdbc:1.3.2")
|
||||
implementation("de.srsoftware:tools.mime:1.1.2")
|
||||
implementation("de.srsoftware:tools.optionals:1.0.0")
|
||||
implementation("de.srsoftware:tools.util:2.0.3")
|
||||
implementation("org.bitbucket.b_c:jose4j:0.9.6")
|
||||
implementation("org.json:json:20240303")
|
||||
implementation("org.xerial:sqlite-jdbc:3.49.0.0")
|
||||
}
|
||||
@@ -1,5 +1,20 @@
|
||||
/* © SRSoftware 2025 */
|
||||
package de.srsoftware.umbrella.legacy;
|
||||
|
||||
import static de.srsoftware.tools.MimeType.MIME_FORM_URL;
|
||||
import static de.srsoftware.tools.Query.decode;
|
||||
import static de.srsoftware.umbrella.core.Constants.*;
|
||||
import static de.srsoftware.umbrella.core.Constants.TOKEN;
|
||||
import static de.srsoftware.umbrella.core.Paths.*;
|
||||
import static de.srsoftware.umbrella.core.Util.request;
|
||||
import static de.srsoftware.umbrella.user.Paths.NOTIFY;
|
||||
import static de.srsoftware.umbrella.user.Paths.VALIDATE_TOKEN;
|
||||
import static java.lang.Long.parseLong;
|
||||
import static java.lang.System.Logger.Level.*;
|
||||
import static java.lang.System.Logger.Level.DEBUG;
|
||||
import static java.net.HttpURLConnection.*;
|
||||
import static java.time.temporal.ChronoUnit.DAYS;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import de.srsoftware.configuration.Configuration;
|
||||
import de.srsoftware.tools.Path;
|
||||
@@ -7,39 +22,36 @@ import de.srsoftware.tools.SessionToken;
|
||||
import de.srsoftware.umbrella.core.BaseHandler;
|
||||
import de.srsoftware.umbrella.core.Token;
|
||||
import de.srsoftware.umbrella.core.UmbrellaException;
|
||||
import de.srsoftware.umbrella.user.model.Session;
|
||||
import de.srsoftware.umbrella.user.model.UmbrellaUser;
|
||||
import de.srsoftware.umbrella.user.sqlite.SqliteDB;
|
||||
|
||||
import static de.srsoftware.tools.MimeType.MIME_FORM_URL;
|
||||
import static de.srsoftware.umbrella.core.ResponseCode.HTTP_SERVER_ERROR;
|
||||
import static de.srsoftware.umbrella.core.Constants.*;
|
||||
import static de.srsoftware.umbrella.core.Constants.KEY;
|
||||
import static de.srsoftware.umbrella.core.Paths.LOGOUT;
|
||||
import static de.srsoftware.umbrella.core.Paths.SEARCH;
|
||||
import static de.srsoftware.umbrella.core.Util.request;
|
||||
import static java.net.HttpURLConnection.*;
|
||||
import static java.time.temporal.ChronoUnit.DAYS;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class LegacyApi extends BaseHandler {
|
||||
|
||||
private final SqliteDB users;
|
||||
private final Configuration config;
|
||||
private final String messageUrl;
|
||||
|
||||
public LegacyApi(SqliteDB userDb, Configuration config) {
|
||||
users = userDb;
|
||||
this.config = config.subset("umbrella.modules").orElseThrow(() -> new RuntimeException("Missing configuration: umbrella.modules"));
|
||||
this.messageUrl = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doGet(Path path, HttpExchange ex) throws IOException {
|
||||
var head = path.pop();
|
||||
return switch (head){
|
||||
case null -> sendRedirect(ex, url(ex).replaceAll("/api/.*",""));
|
||||
case "common_templates" -> {
|
||||
case null -> sendRedirect(ex, url(ex).replaceAll("/legacy/.*",""));
|
||||
case COMMON_TEMPLATES -> {
|
||||
allowOrigin(ex, "*"); // add CORS header
|
||||
yield load(path,ex);
|
||||
}
|
||||
@@ -109,7 +121,7 @@ public class LegacyApi extends BaseHandler {
|
||||
var token = SessionToken.from(ex).map(SessionToken::sessionId)
|
||||
.or(() -> getBearer(ex))
|
||||
.map(Token::of);
|
||||
if (token.isEmpty()) return sendEmptyResponse(HTTP_UNAUTHORIZED,ex);
|
||||
if (token.isEmpty()) return sendRedirect(ex,"/user/login?returnTo="+url);
|
||||
return sendRedirect(ex, url + (url.contains("?") ? "&" : "?") + "token=" + token.get());
|
||||
}
|
||||
|
||||
@@ -121,6 +133,162 @@ public class LegacyApi extends BaseHandler {
|
||||
|
||||
@Override
|
||||
public boolean doPost(Path path, HttpExchange ex) throws IOException {
|
||||
return super.doPost(path, ex);
|
||||
try {
|
||||
return switch (path.pop()) {
|
||||
case JSON -> legacyJson(ex);
|
||||
case NOTIFY -> legacyNotify(ex);
|
||||
case VALIDATE_TOKEN -> validateToken(ex);
|
||||
default -> super.doPost(path,ex);
|
||||
};
|
||||
} catch (UmbrellaException e){
|
||||
return send(ex,e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String stripTrailingSlash(Object o){
|
||||
String url = o.toString();
|
||||
if (url.endsWith("/")) return url.substring(0,url.length()-1);
|
||||
return url;
|
||||
}
|
||||
|
||||
private boolean validateToken(HttpExchange ex) throws UmbrellaException, IOException {
|
||||
String body;
|
||||
try {
|
||||
body = body(ex);
|
||||
} catch (Exception e){
|
||||
throw new UmbrellaException(400,"Failed to read request body").causedBy(e);
|
||||
}
|
||||
var map = decode(body);
|
||||
LOG.log(DEBUG,"validateToken(…, {0}), data: {1}",map);
|
||||
|
||||
String domain = stripTrailingSlash(map.get(DOMAIN) instanceof String s ? s : "");
|
||||
var keys = config.keys();
|
||||
var match = false;
|
||||
for (var key : keys){
|
||||
var baseUrl = config.get(key + ".baseUrl").map(LegacyApi::stripTrailingSlash).orElse(null);
|
||||
if (domain.equals(baseUrl)){
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match) throw new UmbrellaException(500,"Failed to verify request domain!");
|
||||
|
||||
var o = map.get(TOKEN);
|
||||
if (!(o instanceof String token)) throw new UmbrellaException(500,"Request did not contain token!");
|
||||
var session = users.load(Token.of(token));
|
||||
var user = users.load(session);
|
||||
var userMap = user.toMap();
|
||||
userMap.put(TOKEN,Map.of(TOKEN,token,EXPIRATION,session.expiration().getEpochSecond()));
|
||||
return sendContent(ex,userMap);
|
||||
}
|
||||
|
||||
private boolean legacyJson(HttpExchange ex) throws UmbrellaException, IOException {
|
||||
Map<String, Object> data = null;
|
||||
try {
|
||||
data = decode(body(ex));
|
||||
} catch (IOException e) {
|
||||
LOG.log(ERROR,"Failed to extract body of request");
|
||||
throw new UmbrellaException(400,"Failed to extract body of request").causedBy(e);
|
||||
}
|
||||
var arrayPassed = false;
|
||||
var ids = new ArrayList<Long>();
|
||||
for (var entry : data.entrySet()){
|
||||
var key = entry.getKey();
|
||||
var val = entry.getValue();
|
||||
if (key.startsWith("ids[") && (arrayPassed = true)) ids.add(parseLong(entry.getValue().toString()));
|
||||
if ("ids".equals(key) && val instanceof Map<?,?> idMap) {
|
||||
for (var o : idMap.values()){
|
||||
ids.add(Long.parseLong(o.toString()));
|
||||
}
|
||||
arrayPassed = true;
|
||||
}
|
||||
}
|
||||
if (ids.isEmpty() && data.get("id") instanceof String idString) ids.add(parseLong(idString));
|
||||
var related = "true".equals(data.get("related"));
|
||||
if (related) {
|
||||
LOG.log(WARNING,"Fetching related users not implemented, yet!");
|
||||
throw new UmbrellaException(400,"Fetching related users not implemented, yet!");
|
||||
}
|
||||
|
||||
List<UmbrellaUser> userList = new ArrayList<>();
|
||||
if (ids.isEmpty()) {
|
||||
userList = users.list(0, null);
|
||||
} else {
|
||||
for (var id : ids){
|
||||
try {
|
||||
userList.add(users.load(id));
|
||||
} catch (UmbrellaException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (arrayPassed || userList.size() != 1) {
|
||||
Map<Long, Map<String, Object>> userData = userList.stream().collect(Collectors.toMap(UmbrellaUser::id, UmbrellaUser::toMap));
|
||||
return sendContent(ex,userData);
|
||||
}
|
||||
return sendContent(ex, userList.getFirst().toMap());
|
||||
}
|
||||
|
||||
protected Session requestSession(Token token) throws UmbrellaException {
|
||||
var session = users.load(token);
|
||||
session = users.extend(session);
|
||||
return session;
|
||||
}
|
||||
|
||||
private boolean legacyNotify(HttpExchange ex) throws UmbrellaException, IOException {
|
||||
if (messageUrl == null) throw new UmbrellaException(500,"missing configuration: umbrella.modules.message.baseUrl");
|
||||
var mime = contentType(ex).orElse(null);
|
||||
JSONObject data;
|
||||
try {
|
||||
data = switch (mime) {
|
||||
case MIME_FORM_URL -> new JSONObject(decode(body(ex)));
|
||||
case null, default -> json(ex);
|
||||
};
|
||||
} catch (Exception e){
|
||||
throw new UmbrellaException(400,"Failed to fetch content of request").causedBy(e);
|
||||
}
|
||||
if (!data.has(TOKEN)) throw new UmbrellaException(400,"token missing in request data!");
|
||||
var dropKeys = new HashSet<String>();
|
||||
dropKeys.add(TOKEN);
|
||||
var token = Token.of(data.getString(TOKEN));
|
||||
var userSession = requestSession(token);
|
||||
var user = userSession.user();
|
||||
LOG.log(DEBUG,"Token belongs to {0}.",user);
|
||||
|
||||
// recipients is a field used by legacy code.
|
||||
// it may be a single user id or a list of user ids.
|
||||
var recipients = new HashSet<Long>();
|
||||
for (var key : data.keySet()){
|
||||
if (key.startsWith("recipients[")) {
|
||||
recipients.add(data.getLong(key));
|
||||
dropKeys.add(key);
|
||||
break;
|
||||
}
|
||||
if (key.equals("recipients")){
|
||||
var list = data.getJSONObject(key);
|
||||
for (var idx : list.keySet()){
|
||||
var id = list.getLong(idx);
|
||||
recipients.add(id);
|
||||
}
|
||||
dropKeys.add(key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!recipients.isEmpty()){ // replace legacy user ids by user objects in receivers field
|
||||
List<UmbrellaUser> resp = users.list(0, null, recipients.toArray(new Long[0]));
|
||||
data.put("receivers",resp.stream().map(UmbrellaUser::toMap).toList());
|
||||
}
|
||||
|
||||
if (!data.has(SENDER)) data.put(SENDER,user.toMap());
|
||||
|
||||
dropKeys.forEach(data::remove);
|
||||
|
||||
LOG.log(DEBUG,"received legacy notification: {0}",data);
|
||||
|
||||
var messageList = Map.of("messages",List.of(data.toMap()));
|
||||
var resp = request(messageUrl, messageList, null, token.asBearer());
|
||||
if (!(resp instanceof JSONObject json)) throw new UmbrellaException(500,"{0} did not return JSON!",messageUrl);
|
||||
|
||||
// TODO: should we return json?
|
||||
return sendEmptyResponse(HTTP_OK,ex);
|
||||
}
|
||||
}
|
||||
|
||||
19
legacy/src/main/resources/css/basic/style.css
Normal file
19
legacy/src/main/resources/css/basic/style.css
Normal file
@@ -0,0 +1,19 @@
|
||||
@font-face {
|
||||
font-family: "awesome";
|
||||
src: url("../fontawesome-webfont.woff");
|
||||
}
|
||||
.symbol{
|
||||
font-family: awesome;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.completed > a::before {
|
||||
content: " ✓";
|
||||
}
|
||||
|
||||
.canceled a,
|
||||
.children .inactive,
|
||||
.requirements .inactive,
|
||||
.tasks .inactive{
|
||||
text-decoration: line-through;
|
||||
}
|
||||
84
legacy/src/main/resources/css/bloodshed/colors.css
Normal file
84
legacy/src/main/resources/css/bloodshed/colors.css
Normal file
@@ -0,0 +1,84 @@
|
||||
body{
|
||||
color: red;
|
||||
background-color: black;
|
||||
}
|
||||
a{
|
||||
color: red;
|
||||
}
|
||||
|
||||
.tasks .pending>a{
|
||||
color: gray;
|
||||
}
|
||||
|
||||
.completed>a,
|
||||
.started>a{
|
||||
color: #5bc500;
|
||||
}
|
||||
|
||||
.canceled>a{
|
||||
color: gray;
|
||||
}
|
||||
|
||||
tr{
|
||||
background-color: #52525270;
|
||||
}
|
||||
tr:nth-child(2n+1){
|
||||
background-color: #a7a7a740;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
border-color: red;
|
||||
}
|
||||
|
||||
.button,
|
||||
input,
|
||||
button {
|
||||
background-color: red;
|
||||
color: #303030;
|
||||
}
|
||||
.hover{
|
||||
background-color: black;
|
||||
border-color: red;
|
||||
}
|
||||
|
||||
form.invoice textarea {
|
||||
background-color: red;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.infos span{
|
||||
background-color: #00ad00;
|
||||
color: black;
|
||||
}
|
||||
.errors span{
|
||||
background-color: #ff9847;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.warnings span{
|
||||
background-color: #ffff00;
|
||||
color: black;
|
||||
}
|
||||
|
||||
#announce{
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
#main_menu .button:hover {
|
||||
color: red;
|
||||
background-color: gray;
|
||||
}
|
||||
|
||||
tr:hover td{
|
||||
background-color: #160202;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: black;
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.description img,
|
||||
#preview img{
|
||||
background: lightcyan;
|
||||
}
|
||||
584
legacy/src/main/resources/css/bloodshed/style.css
Normal file
584
legacy/src/main/resources/css/bloodshed/style.css
Normal file
@@ -0,0 +1,584 @@
|
||||
@font-face {
|
||||
font-family: "awesome";
|
||||
src: url("../fontawesome-webfont.woff");
|
||||
}
|
||||
*{
|
||||
max-width:100%;
|
||||
min-width:auto;
|
||||
}
|
||||
a{
|
||||
text-decoration: none;
|
||||
}
|
||||
body{
|
||||
font-family: sans-serif;
|
||||
}
|
||||
code {
|
||||
display: inline-block;
|
||||
padding: 5px;
|
||||
margin: 5px 0;
|
||||
}
|
||||
textarea {
|
||||
min-height: 60px;
|
||||
width: 100%;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
textarea:hover{
|
||||
min-height: 400px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input[type=text],input[type=email]{
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
td.connectors form input{
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
img#logo{
|
||||
position: fixed;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
blockquote{
|
||||
font-style: italic;
|
||||
}
|
||||
#main_menu,
|
||||
#main_menu form{
|
||||
display: inline;
|
||||
}
|
||||
|
||||
#main_menu .button {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
margin-top: 7px;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.hidden{
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.hover:hover .hidden{
|
||||
display: inherit !important;
|
||||
}
|
||||
|
||||
.emphasized{
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.add_positions,
|
||||
.requirements{
|
||||
max-height: 60px;
|
||||
max-width: 66%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.add_positions:hover,
|
||||
.requirements:hover{
|
||||
max-height: 999999px;
|
||||
max-width: 999999px;
|
||||
}
|
||||
|
||||
.add_positions > ul > li{
|
||||
max-height: 20px;
|
||||
overflow: hidden;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.add_positions > ul > li:hover{
|
||||
max-height: 999999px;
|
||||
}
|
||||
|
||||
fieldset.add.document:hover {
|
||||
max-height: unset;
|
||||
}
|
||||
|
||||
fieldset.add.document {
|
||||
max-height: 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
fieldset.options label,
|
||||
.add_positions label span,
|
||||
.poll_status label,
|
||||
.requirements label{
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tasks .canceled a,
|
||||
.children .inactive,
|
||||
.requirements .inactive,
|
||||
.tasks .inactive{
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.tasks .navi a{
|
||||
display: inline-table;
|
||||
}
|
||||
|
||||
blockquote a,
|
||||
p a,
|
||||
.description a{
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
tr > *{
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
label.street:after,
|
||||
label.location::after {
|
||||
content: "\a ";
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.poll.evaluate table tr:nth-child(n+2) th,
|
||||
.poll.evaluate table td{
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.poll .disabled{
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.company div > fieldset,
|
||||
.company > table{
|
||||
margin-right: 10px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
table.time h2{
|
||||
margin-right: 60px;
|
||||
}
|
||||
|
||||
.file label,
|
||||
.document .dates label{
|
||||
display: block
|
||||
}
|
||||
.document .header,
|
||||
.document .tags{
|
||||
clear: both;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
border-radius: 10px;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
.contacts fieldset,
|
||||
.document .customer,
|
||||
.document .document_type,
|
||||
.document .sender,
|
||||
.document .court,
|
||||
.document .dates,
|
||||
.document .template,
|
||||
fieldset.del_note,
|
||||
fieldset.login_service_list,
|
||||
fieldset.userlist,
|
||||
fieldset.document.list{
|
||||
float: left;
|
||||
}
|
||||
|
||||
.document .dates label{
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.button,
|
||||
input,
|
||||
button {
|
||||
border: 0 none;
|
||||
font-weight: bold;
|
||||
margin: 0 2px;
|
||||
padding: 3px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.change_state,
|
||||
td.connectors button[type="submit"],
|
||||
.prop_action,
|
||||
input[type="submit"] {
|
||||
clear: both;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.symbol{
|
||||
font-family: awesome;
|
||||
font-size: 20px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
#main_menu .symbol{
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.right{
|
||||
float: right;
|
||||
margin: 0 0 0 5px;
|
||||
}
|
||||
|
||||
.tasks .project {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.right-abs {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
}
|
||||
.right-fix {
|
||||
position: fixed;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
}
|
||||
.hover {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-radius: 5px;
|
||||
max-height: 35px;
|
||||
overflow: hidden;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
|
||||
.hover:hover {
|
||||
max-height: unset;
|
||||
}
|
||||
|
||||
.hover_h > a:nth-child(n+2){
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hover_h:hover {
|
||||
padding: 5px 0 20px;
|
||||
}
|
||||
|
||||
.hover_h:hover > a:nth-child(n+2){
|
||||
display: inherit;
|
||||
}
|
||||
|
||||
form.document textarea {
|
||||
font-weight: bold;
|
||||
min-height: 90px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pos_price,
|
||||
form.document input.price,
|
||||
form.document input.amount{
|
||||
max-width: 60px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
form.document .tax{
|
||||
min-width: 70px;
|
||||
}
|
||||
form.document .tax input{
|
||||
max-width: 40px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
td > h1{
|
||||
display: inline-block;
|
||||
margin: 0 10px 0 0;
|
||||
}
|
||||
|
||||
.center,
|
||||
.infos,
|
||||
.errors,
|
||||
.warnings{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.infos span,
|
||||
.errors span,
|
||||
.warnings span{
|
||||
margin: 5px 0 0;
|
||||
padding: 5px;
|
||||
border-radius: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.tags span{
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
.bookmark .share,
|
||||
.bookmark .tags{
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.bookmark form .share{
|
||||
height: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bookmark form .share:hover{
|
||||
height: initial;
|
||||
}
|
||||
|
||||
fieldset.bookmark > fieldset{
|
||||
max-height: 14px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
fieldset.bookmark > fieldset.tags{
|
||||
max-height: unset;
|
||||
}
|
||||
|
||||
fieldset.bookmark > fieldset:hover{
|
||||
max-height: unset;
|
||||
}
|
||||
.bookmark>fieldset>a{
|
||||
word-wrap: break-word;
|
||||
display: block;
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.bookmark>fieldset>a.button{
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.copytext{
|
||||
position:fixed;
|
||||
width: 100px;
|
||||
left: -1000px;
|
||||
}
|
||||
|
||||
img[src*="pos=right"] {
|
||||
float:right;
|
||||
margin: 5px 0 5px 5px;
|
||||
}
|
||||
|
||||
img[src*="pos=left"] {
|
||||
float:left;
|
||||
margin: 5px 5px 5px 0;
|
||||
}
|
||||
|
||||
img[src*="width=100"]{
|
||||
max-width: 100px;
|
||||
}
|
||||
img[src*="width=200"]{
|
||||
max-width: 200px;
|
||||
}
|
||||
img[src*="width=300"]{
|
||||
max-width: 300px;
|
||||
}
|
||||
img[src*="width=400"]{
|
||||
max-width: 400px;
|
||||
}
|
||||
img[src*="width=500"]{
|
||||
max-width: 500px;
|
||||
}
|
||||
img[src*="width=600"]{
|
||||
max-width: 600px;
|
||||
}
|
||||
img[src*="width=700"]{
|
||||
max-width: 700px;
|
||||
}
|
||||
img[src*="width=800"]{
|
||||
max-width: 800px;
|
||||
}
|
||||
img[src*="width=900"]{
|
||||
max-width: 900px;
|
||||
}
|
||||
img[src*="width=50%"]{
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
img[src*="width=33%"]{
|
||||
max-width: 33%;
|
||||
}
|
||||
|
||||
img[src$="width=25%"]{
|
||||
max-width: 25%;
|
||||
}
|
||||
|
||||
#announce{
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
padding: 3px 5px 0;
|
||||
border-top-left-radius: 7px;
|
||||
}
|
||||
|
||||
fieldset.scrolling{
|
||||
overflow: scroll;
|
||||
max-height: 80%;
|
||||
}
|
||||
|
||||
svg#gantt{
|
||||
max-width: unset;
|
||||
}
|
||||
|
||||
svg .row{
|
||||
fill: none;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
svg .row:hover{
|
||||
fill: #333333;
|
||||
}
|
||||
|
||||
svg .duration{
|
||||
fill:rgba(255,0,255,0.4);
|
||||
stroke:none;
|
||||
}
|
||||
|
||||
svg .schedule{
|
||||
fill:none;
|
||||
stroke-width:1;
|
||||
stroke: yellow;
|
||||
}
|
||||
|
||||
svg .start{
|
||||
stroke-dasharray: 10,30,40;
|
||||
}
|
||||
|
||||
svg .stop{
|
||||
stroke-dasharray: 50,30;
|
||||
}
|
||||
|
||||
svg text{
|
||||
stroke: none;
|
||||
fill: red;
|
||||
}
|
||||
|
||||
div.search{
|
||||
display: inline-block;
|
||||
max-width: 150px;
|
||||
}
|
||||
|
||||
div.search input,
|
||||
div.search label{
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.search:hover input,
|
||||
div.search:hover label{
|
||||
display: initial;
|
||||
}
|
||||
|
||||
.note td.code {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.note td.code textarea {
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.project-index td .users{
|
||||
max-height: 30px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.project-index tr:hover td .users{
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.easylist fieldset a.button {
|
||||
width: 80%;
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#preview,
|
||||
#preview-source{
|
||||
display: inline-block;
|
||||
max-width: calc(50% - 20px);
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
table #preview,
|
||||
table #preview-source{
|
||||
display: inherit;
|
||||
max-width: unset;
|
||||
vertical-align: inherit;
|
||||
}
|
||||
|
||||
#preview.loading{
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.completed > a::before {
|
||||
content: " ✓";
|
||||
}
|
||||
|
||||
@media (max-width:1199px) {
|
||||
*[hide="12"]{
|
||||
display: none;
|
||||
}
|
||||
table.document input{
|
||||
max-width: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px){
|
||||
*[hide="9"]{
|
||||
display: none;
|
||||
}
|
||||
table.document input{
|
||||
max-width: 60px;
|
||||
}
|
||||
table.document .tax input,
|
||||
table.document input.amount{
|
||||
max-width: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 800px){
|
||||
*[hide="8"]{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 700px){
|
||||
*[hide="7"]{
|
||||
display: none;
|
||||
}
|
||||
fieldset.bookmark > fieldset{
|
||||
max-height: 50px;
|
||||
}
|
||||
#preview,
|
||||
#preview-source{
|
||||
display: block;
|
||||
max-width: unset;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px){
|
||||
*[hide="6"]{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.easylist fieldset a.button {
|
||||
width: 60%;
|
||||
}
|
||||
}
|
||||
@media (max-width: 500px){
|
||||
*[hide="5"]{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media (max-width: 400px){
|
||||
*[hide="4"]{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media print{
|
||||
fieldset.note span.right,
|
||||
#logo, #main_menu {
|
||||
display: none;
|
||||
}
|
||||
fieldset.process, fieldset.database{
|
||||
page-break-after: always;
|
||||
}
|
||||
.export>fieldset{
|
||||
border: 0 none;
|
||||
}
|
||||
}
|
||||
41
legacy/src/main/resources/css/bloodshed/svg_colors.css
Normal file
41
legacy/src/main/resources/css/bloodshed/svg_colors.css
Normal file
@@ -0,0 +1,41 @@
|
||||
.arrow{
|
||||
stroke: red;
|
||||
}
|
||||
circle, ellipse, rect{
|
||||
stroke: black;
|
||||
}
|
||||
ellipse,
|
||||
circle.process{
|
||||
fill: rgba(255,255,255,0.5);
|
||||
}
|
||||
rect.terminal{
|
||||
fill: white;
|
||||
}
|
||||
circle.connector{
|
||||
fill: rgba(0,0,0,0.05);
|
||||
stroke: none;
|
||||
}
|
||||
circle.connector:hover{
|
||||
fill: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
#backdrop{
|
||||
fill: transparent;
|
||||
}
|
||||
|
||||
.arrow circle{
|
||||
stroke: none;
|
||||
fill: rgba(128,128,128,0.5);
|
||||
}
|
||||
.arrow text{
|
||||
fill: black;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.arrow:hover circle{
|
||||
fill: rgba(255,0,0,0.3);
|
||||
}
|
||||
|
||||
.process text{
|
||||
fill: black;
|
||||
}
|
||||
76
legacy/src/main/resources/css/comfort/colors.css
Normal file
76
legacy/src/main/resources/css/comfort/colors.css
Normal file
@@ -0,0 +1,76 @@
|
||||
body{
|
||||
background: #0f3a09;
|
||||
}
|
||||
a{
|
||||
color: #0c0061;
|
||||
}
|
||||
|
||||
code{
|
||||
background: #cccccc;
|
||||
border-color: #0f3a09;
|
||||
}
|
||||
|
||||
.pending a{
|
||||
color: #45513a;
|
||||
}
|
||||
.open>a{
|
||||
color: #0f3a09;
|
||||
}
|
||||
|
||||
.completed>a:after{
|
||||
content: " ✓";
|
||||
}
|
||||
|
||||
.canceled>a{
|
||||
color: #414141;
|
||||
}
|
||||
|
||||
li:hover > a,
|
||||
a:hover{
|
||||
color: red;
|
||||
}
|
||||
|
||||
fieldset,table{
|
||||
background: rgba(255,255,255,0.49);
|
||||
}
|
||||
|
||||
legend{
|
||||
background: #82987f;
|
||||
}
|
||||
|
||||
.share, .share>legend{
|
||||
background:#8cff7e;
|
||||
}
|
||||
|
||||
.button{
|
||||
background: white;
|
||||
}
|
||||
|
||||
#announce a{
|
||||
color:black;
|
||||
}
|
||||
|
||||
table.diagram .step,
|
||||
body > a.symbol {
|
||||
background: white;
|
||||
}
|
||||
|
||||
.right a {
|
||||
background: white;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.infos span{
|
||||
background: lime;
|
||||
}
|
||||
.warnings span{
|
||||
background: yellow;
|
||||
}
|
||||
|
||||
.errors span{
|
||||
background: orange;
|
||||
}
|
||||
|
||||
.search label{
|
||||
color: white;
|
||||
}
|
||||
526
legacy/src/main/resources/css/comfort/style.css
Normal file
526
legacy/src/main/resources/css/comfort/style.css
Normal file
@@ -0,0 +1,526 @@
|
||||
@font-face {
|
||||
font-family: "awesome";
|
||||
src: url("../fontawesome-webfont.woff");
|
||||
}
|
||||
a{
|
||||
text-decoration:none;
|
||||
}
|
||||
body{
|
||||
margin-left: 160px;
|
||||
margin-top: 25px;
|
||||
font-family: sans-serif;
|
||||
position: relative;
|
||||
}
|
||||
code{
|
||||
display: inline-block;
|
||||
padding: 5px;
|
||||
border: 1px solid;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
body > a.symbol {
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
body > h2{
|
||||
color: #67a05f;
|
||||
}
|
||||
|
||||
body.user.login{
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
img#logo{
|
||||
position: fixed;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
blockquote{
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#main_menu{
|
||||
position: fixed;
|
||||
top:0;
|
||||
left:0;
|
||||
bottom:0;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
#main_menu a{
|
||||
display: block;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hidden{
|
||||
display: none;
|
||||
}
|
||||
|
||||
td{
|
||||
position: relative;
|
||||
}
|
||||
|
||||
fieldset{
|
||||
position: relative;
|
||||
margin-top: 5px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.hover:hover .hidden{
|
||||
display: inherit;
|
||||
}
|
||||
|
||||
.button{
|
||||
padding:2px;
|
||||
margin: 1px 0;
|
||||
border-radius: 5px;
|
||||
border: 1px solid black;
|
||||
white-space: pre;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.symbol{
|
||||
font-family: awesome;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.right{
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.right a {
|
||||
border: 1px solid;
|
||||
border-radius: 5px;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
|
||||
.right-fix{
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 0;
|
||||
overflow: hidden;
|
||||
max-height: 25px;
|
||||
border: 1px solid red;
|
||||
border-radius: 5px;
|
||||
background: white;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.canceled{
|
||||
text-decoration: line-through
|
||||
}
|
||||
|
||||
.hover:hover{
|
||||
max-height: unset !important;
|
||||
max-width: unset !important;
|
||||
}
|
||||
|
||||
fieldset textarea{
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
font-size: 16px;
|
||||
}
|
||||
img{
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.infos {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.errors span,
|
||||
.infos span{
|
||||
margin: 2px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.copytext{
|
||||
position: fixed;
|
||||
width: 90px;
|
||||
left: -100px;
|
||||
}
|
||||
|
||||
.poll td>*{
|
||||
margin: 15px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.poll td.poll_status>*{
|
||||
margin: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.poll td.poll_status>a{
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.poll table{
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.poll tr{
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.poll td{
|
||||
border-style: solid;
|
||||
border-width: 1px 0 0 0;
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
.poll .disabled{
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
img[src*="pos=right"] {
|
||||
float:right;
|
||||
margin: 5px 0 5px 5px;
|
||||
}
|
||||
|
||||
img[src*="pos=left"] {
|
||||
float:left;
|
||||
margin: 5px 5px 5px 0;
|
||||
}
|
||||
|
||||
img[src*="width=100"]{
|
||||
max-width: 100px;
|
||||
}
|
||||
img[src*="width=200"]{
|
||||
max-width: 200px;
|
||||
}
|
||||
img[src*="width=300"]{
|
||||
max-width: 300px;
|
||||
}
|
||||
img[src*="width=400"]{
|
||||
max-width: 400px;
|
||||
}
|
||||
img[src*="width=500"]{
|
||||
max-width: 500px;
|
||||
}
|
||||
img[src*="width=600"]{
|
||||
max-width: 600px;
|
||||
}
|
||||
img[src*="width=700"]{
|
||||
max-width: 700px;
|
||||
}
|
||||
img[src*="width=800"]{
|
||||
max-width: 800px;
|
||||
}
|
||||
img[src*="width=900"]{
|
||||
max-width: 900px;
|
||||
}
|
||||
img[src*="width=50%"]{
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
img[src*="width=33%"]{
|
||||
max-width: 33%;
|
||||
}
|
||||
|
||||
img[src$="width=25%"]{
|
||||
max-width: 25%;
|
||||
}
|
||||
|
||||
#announce{
|
||||
position: absolute;
|
||||
right: 3px;
|
||||
bottom: -40px;
|
||||
}
|
||||
|
||||
span.hover_h {
|
||||
display: block;
|
||||
}
|
||||
|
||||
label{
|
||||
display: block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
legend{
|
||||
padding: 0 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.search input {
|
||||
margin: 5px 0 0 5px;
|
||||
width: 62%;
|
||||
}
|
||||
|
||||
.search label input{
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.search label{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.search:hover label{
|
||||
display: block;
|
||||
}
|
||||
|
||||
.search button{
|
||||
width: 40px;
|
||||
font-size: 14px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.share{
|
||||
height: 35px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.share:hover{
|
||||
height: inherit;
|
||||
}
|
||||
|
||||
.bookmark textarea[name=comment]{
|
||||
max-height: 50px;
|
||||
}
|
||||
|
||||
.bookmark textarea[name=comment]:hover{
|
||||
max-height: unset;
|
||||
}
|
||||
|
||||
.bookmark fieldset input{
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.notes .note{
|
||||
border: 1px solid;
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
.warnings span{
|
||||
background: yellow;
|
||||
}
|
||||
|
||||
.errors span{
|
||||
background: orange;
|
||||
}
|
||||
|
||||
.tasks li{
|
||||
margin: 5px;
|
||||
padding-right: 80px;
|
||||
}
|
||||
.project-view{
|
||||
min-width: 100%;
|
||||
}
|
||||
.project-view span.hover_h{
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.note td.code {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.note td.code textarea {
|
||||
min-height: 200px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.add_positions > ul,
|
||||
.requirements > ul {
|
||||
max-height: 400px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
td input,
|
||||
table.vertical{
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.document td label{
|
||||
width: unset;
|
||||
}
|
||||
|
||||
.document.view .add_positions img{
|
||||
display: none;
|
||||
}
|
||||
|
||||
td input[type=checkbox]{
|
||||
width: unset;
|
||||
}
|
||||
|
||||
table.vertical span.right{
|
||||
position: fixed;
|
||||
top: 25px;
|
||||
left: 60px;
|
||||
right: 5px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
img[src*="pos=right"] {
|
||||
float:right;
|
||||
margin: 5px 0 5px 5px;
|
||||
}
|
||||
|
||||
img[src*="pos=left"] {
|
||||
float:left;
|
||||
margin: 5px 5px 5px 0;
|
||||
}
|
||||
|
||||
img[src*="width=100"]{
|
||||
max-width: 100px;
|
||||
}
|
||||
img[src*="width=200"]{
|
||||
max-width: 200px;
|
||||
}
|
||||
img[src*="width=300"]{
|
||||
max-width: 300px;
|
||||
}
|
||||
img[src*="width=400"]{
|
||||
max-width: 400px;
|
||||
}
|
||||
img[src*="width=500"]{
|
||||
max-width: 500px;
|
||||
}
|
||||
img[src*="width=600"]{
|
||||
max-width: 600px;
|
||||
}
|
||||
img[src*="width=700"]{
|
||||
max-width: 700px;
|
||||
}
|
||||
img[src*="width=800"]{
|
||||
max-width: 800px;
|
||||
}
|
||||
img[src*="width=900"]{
|
||||
max-width: 900px;
|
||||
}
|
||||
img[src*="width=50%"]{
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
img[src*="width=33%"]{
|
||||
max-width: 33%;
|
||||
}
|
||||
|
||||
img[src$="width=25%"]{
|
||||
max-width: 25%;
|
||||
}
|
||||
|
||||
#preview,
|
||||
#preview-source{
|
||||
display: inline-block;
|
||||
max-width: calc(50% - 20px);
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
table #preview,
|
||||
table #preview-source{
|
||||
display: inherit;
|
||||
max-width: unset;
|
||||
vertical-align: inherit;
|
||||
}
|
||||
|
||||
#preview.loading{
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.easylist fieldset a.button {
|
||||
width: 70%;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
@media (min-width: 1100px){
|
||||
.share{
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1000px){
|
||||
*[hide="12"]{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px){
|
||||
*[hide="11"]{
|
||||
display: none;
|
||||
}
|
||||
#preview,
|
||||
#preview-source{
|
||||
display: block;
|
||||
max-width: unset;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 800px){
|
||||
*[hide="10"]{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 700px){
|
||||
*[hide="9"]{
|
||||
display: none;
|
||||
}
|
||||
body{
|
||||
margin-left: 50px;
|
||||
margin-top: 60px;
|
||||
}
|
||||
#main_menu{
|
||||
max-width: 50px;
|
||||
z-index: 10;
|
||||
overflow: hidden;
|
||||
background: inherit;
|
||||
}
|
||||
#main_menu:hover{
|
||||
max-width: unset;
|
||||
}
|
||||
#main_menu .button{
|
||||
padding: 6px 0;
|
||||
}
|
||||
.search button{
|
||||
display: none;
|
||||
}
|
||||
.search:hover button{
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px){
|
||||
*[hide="8"]{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 500px){
|
||||
*[hide="7"]{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px){
|
||||
*[hide="6"]{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media print{
|
||||
fieldset.note span.right,
|
||||
#logo, #main_menu {
|
||||
display: none;
|
||||
}
|
||||
fieldset.process, fieldset.database{
|
||||
page-break-after: always;
|
||||
}
|
||||
.export>fieldset{
|
||||
border: 0 none;
|
||||
}
|
||||
}
|
||||
37
legacy/src/main/resources/css/comfort/svg_colors.css
Normal file
37
legacy/src/main/resources/css/comfort/svg_colors.css
Normal file
@@ -0,0 +1,37 @@
|
||||
.arrow{
|
||||
stroke: blue;
|
||||
}
|
||||
circle, ellipse, rect{
|
||||
stroke: black;
|
||||
}
|
||||
ellipse,
|
||||
circle.process{
|
||||
fill: rgba(255,255,255,0.5);
|
||||
}
|
||||
rect.terminal{
|
||||
fill: white;
|
||||
}
|
||||
circle.connector{
|
||||
fill: rgba(0,0,0,0.05);
|
||||
stroke: none;
|
||||
}
|
||||
circle.connector:hover{
|
||||
fill: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
#backdrop{
|
||||
fill: transparent;
|
||||
}
|
||||
|
||||
.arrow circle{
|
||||
stroke: none;
|
||||
fill: white;
|
||||
}
|
||||
.arrow text{
|
||||
fill: blue;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.arrow:hover circle{
|
||||
fill: rgba(0,0,255,0.3);
|
||||
}
|
||||
BIN
legacy/src/main/resources/css/fontawesome-webfont.woff
Normal file
BIN
legacy/src/main/resources/css/fontawesome-webfont.woff
Normal file
Binary file not shown.
23
legacy/src/main/resources/css/style.css
Normal file
23
legacy/src/main/resources/css/style.css
Normal file
@@ -0,0 +1,23 @@
|
||||
a {
|
||||
color: palegreen;
|
||||
}
|
||||
body {
|
||||
background: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.error{
|
||||
background: red;
|
||||
color: black;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.error button{
|
||||
margin: 2px 5px;
|
||||
}
|
||||
#navi .button{
|
||||
border: 1px solid red;
|
||||
padding: 2px;
|
||||
margin: 2px;
|
||||
text-decoration: none;
|
||||
}
|
||||
21
legacy/src/main/resources/css/svg_common.css
Normal file
21
legacy/src/main/resources/css/svg_common.css
Normal file
@@ -0,0 +1,21 @@
|
||||
.model svg{
|
||||
max-width: 100%;
|
||||
max-height: 1000px;
|
||||
}
|
||||
.arrow{
|
||||
stroke-width: 2;
|
||||
}
|
||||
circle, ellipse, rect{
|
||||
stroke-width: 2;
|
||||
}
|
||||
circle.connector:hover{
|
||||
r: 30;
|
||||
}
|
||||
text{
|
||||
text-anchor:middle;
|
||||
alignment-baseline:middle;
|
||||
font-size:12;
|
||||
}
|
||||
.arrow:hover circle{
|
||||
r: 50;
|
||||
}
|
||||
80
legacy/src/main/resources/css/winter/colors.css
Normal file
80
legacy/src/main/resources/css/winter/colors.css
Normal file
@@ -0,0 +1,80 @@
|
||||
body{
|
||||
color: black;
|
||||
background-color: white;
|
||||
}
|
||||
a{
|
||||
color: #005678;
|
||||
}
|
||||
|
||||
.tasks .pending>a{
|
||||
color: #888888;
|
||||
}
|
||||
|
||||
.completed>a,
|
||||
.started>a{
|
||||
color: #53822c;
|
||||
}
|
||||
|
||||
.canceled>a{
|
||||
color: gray;
|
||||
}
|
||||
|
||||
tr{
|
||||
background-color: #dadada91;
|
||||
}
|
||||
tr:nth-child(2n+1){
|
||||
background-color: #b9b9b982;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
.button,
|
||||
input,
|
||||
button {
|
||||
background-color: black;
|
||||
color: #d3d3d3;
|
||||
}
|
||||
.hover{
|
||||
background-color: white;
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
form.invoice textarea {
|
||||
background-color: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.infos span{
|
||||
background-color: #00ad00;
|
||||
color: white;
|
||||
}
|
||||
.errors span{
|
||||
background-color: #ff9847;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.hit,
|
||||
.warnings span{
|
||||
background-color: #ffff00;
|
||||
color: black;
|
||||
}
|
||||
|
||||
#announce{
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#main_menu .button:hover {
|
||||
color: white;
|
||||
background-color: gray;
|
||||
}
|
||||
|
||||
tr:hover td{
|
||||
background-color: #edf9f9;
|
||||
}
|
||||
|
||||
code {
|
||||
background-color: black;
|
||||
color: lime;
|
||||
}
|
||||
597
legacy/src/main/resources/css/winter/style.css
Normal file
597
legacy/src/main/resources/css/winter/style.css
Normal file
@@ -0,0 +1,597 @@
|
||||
@font-face {
|
||||
font-family: "awesome";
|
||||
src: url("../fontawesome-webfont.woff");
|
||||
}
|
||||
*{
|
||||
max-width:100%;
|
||||
min-width:auto;
|
||||
}
|
||||
a{
|
||||
text-decoration: none;
|
||||
}
|
||||
body{
|
||||
font-family: sans-serif;
|
||||
}
|
||||
code {
|
||||
display: inline-block;
|
||||
padding: 5px;
|
||||
margin: 5px 0;
|
||||
}
|
||||
textarea {
|
||||
min-height: 60px;
|
||||
width: 100%;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
textarea:hover{
|
||||
min-height: 400px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
input[type=text]{
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
td.connectors form input{
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
img#logo{
|
||||
position: fixed;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
blockquote{
|
||||
font-style: italic;
|
||||
}
|
||||
#main_menu,
|
||||
#main_menu form{
|
||||
display: block;
|
||||
}
|
||||
|
||||
#main_menu .button {
|
||||
font-size: 12px;
|
||||
font-weight: normal;
|
||||
margin-top: 7px;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.hidden{
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.hover:hover .hidden{
|
||||
display: inherit !important;
|
||||
}
|
||||
|
||||
.emphasized{
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.add_positions,
|
||||
.requirements{
|
||||
max-height: 60px;
|
||||
max-width: 66%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.add_positions:hover,
|
||||
.requirements:hover{
|
||||
max-height: 999999px;
|
||||
max-width: 999999px;
|
||||
}
|
||||
|
||||
.add_positions > ul > li{
|
||||
max-height: 20px;
|
||||
overflow: hidden;
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.add_positions > ul > li:hover{
|
||||
max-height: 999999px;
|
||||
}
|
||||
|
||||
fieldset.add.document:hover {
|
||||
max-height: unset;
|
||||
}
|
||||
|
||||
fieldset.add.document {
|
||||
max-height: 5px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
fieldset.options label,
|
||||
.add_positions label span,
|
||||
.poll_status label,
|
||||
.requirements label{
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tasks .canceled a,
|
||||
.children .inactive,
|
||||
.requirements .inactive,
|
||||
.tasks .inactive{
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.tasks .navi a{
|
||||
display: inline-table;
|
||||
}
|
||||
|
||||
blockquote a,
|
||||
p a,
|
||||
.description a{
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
tr > *{
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
label.street:after,
|
||||
label.location::after {
|
||||
content: "\a ";
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
.poll.evaluate table tr:nth-child(n+2) th,
|
||||
.poll.evaluate table td{
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.poll .disabled{
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.company div > fieldset,
|
||||
.company > table{
|
||||
margin-right: 10px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
table.time h2{
|
||||
margin-right: 60px;
|
||||
}
|
||||
|
||||
.file label,
|
||||
.document .dates label{
|
||||
display: block
|
||||
}
|
||||
.clear,
|
||||
.document .header,
|
||||
.document .tags{
|
||||
clear: both;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
border-radius: 10px;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
.contacts fieldset,
|
||||
.document .customer,
|
||||
.document .document_type,
|
||||
.document .sender,
|
||||
.document .court,
|
||||
.document .dates,
|
||||
.document .template,
|
||||
fieldset.del_note,
|
||||
fieldset.login_service_list,
|
||||
fieldset.userlist,
|
||||
fieldset.document.list{
|
||||
float: left;
|
||||
}
|
||||
|
||||
.document .dates label{
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.button,
|
||||
input,
|
||||
button {
|
||||
border: 0 none;
|
||||
font-weight: bold;
|
||||
margin: 0 2px;
|
||||
padding: 3px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.change_state,
|
||||
td.connectors button[type="submit"],
|
||||
.prop_action,
|
||||
input[type="submit"] {
|
||||
clear: both;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.symbol{
|
||||
font-family: awesome;
|
||||
font-size: 20px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
#main_menu .symbol{
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.right{
|
||||
float: right;
|
||||
margin: 0 0 0 5px;
|
||||
}
|
||||
|
||||
.tasks .project {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.right-abs {
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
}
|
||||
.right-fix {
|
||||
position: fixed;
|
||||
right: 5px;
|
||||
top: 5px;
|
||||
}
|
||||
.hover {
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
border-radius: 5px;
|
||||
max-height: 35px;
|
||||
overflow: hidden;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
|
||||
.hover:hover {
|
||||
max-height: unset;
|
||||
}
|
||||
|
||||
.hover_h > a:nth-child(n+2){
|
||||
display: none;
|
||||
}
|
||||
|
||||
.hover_h:hover {
|
||||
padding: 5px 0 20px;
|
||||
}
|
||||
|
||||
.hover_h:hover > a:nth-child(n+2){
|
||||
display: inherit;
|
||||
}
|
||||
|
||||
form.document textarea {
|
||||
font-weight: bold;
|
||||
min-height: 90px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.pos_price,
|
||||
form.document input.price,
|
||||
form.document input.amount{
|
||||
max-width: 60px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
form.document .tax{
|
||||
min-width: 70px;
|
||||
}
|
||||
form.document .tax input{
|
||||
max-width: 40px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
td > h1{
|
||||
display: inline-block;
|
||||
margin: 0 10px 0 0;
|
||||
}
|
||||
|
||||
.center,
|
||||
.infos,
|
||||
.errors,
|
||||
.warnings{
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.infos span,
|
||||
.errors span,
|
||||
.warnings span{
|
||||
margin: 5px 0 0;
|
||||
padding: 5px;
|
||||
border-radius: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.tags span{
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
.bookmark .share,
|
||||
.bookmark .tags{
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.bookmark form .share{
|
||||
height: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bookmark form .share:hover{
|
||||
height: initial;
|
||||
}
|
||||
|
||||
fieldset.bookmark > fieldset{
|
||||
max-height: 14px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
fieldset.bookmark > fieldset.tags{
|
||||
max-height: unset;
|
||||
}
|
||||
|
||||
fieldset.bookmark > fieldset:hover{
|
||||
max-height: unset;
|
||||
}
|
||||
.bookmark>fieldset>a{
|
||||
word-wrap: break-word;
|
||||
display: block;
|
||||
min-width: auto;
|
||||
}
|
||||
|
||||
.bookmark>fieldset>a.button{
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.copytext{
|
||||
position:fixed;
|
||||
width: 100px;
|
||||
left: -1000px;
|
||||
}
|
||||
|
||||
img[src*="pos=right"] {
|
||||
float:right;
|
||||
margin: 5px 0 5px 5px;
|
||||
}
|
||||
|
||||
img[src*="pos=left"] {
|
||||
float:left;
|
||||
margin: 5px 5px 5px 0;
|
||||
}
|
||||
|
||||
img[src*="width=100"]{
|
||||
max-width: 100px;
|
||||
}
|
||||
img[src*="width=200"]{
|
||||
max-width: 200px;
|
||||
}
|
||||
img[src*="width=300"]{
|
||||
max-width: 300px;
|
||||
}
|
||||
img[src*="width=400"]{
|
||||
max-width: 400px;
|
||||
}
|
||||
img[src*="width=500"]{
|
||||
max-width: 500px;
|
||||
}
|
||||
img[src*="width=600"]{
|
||||
max-width: 600px;
|
||||
}
|
||||
img[src*="width=700"]{
|
||||
max-width: 700px;
|
||||
}
|
||||
img[src*="width=800"]{
|
||||
max-width: 800px;
|
||||
}
|
||||
img[src*="width=900"]{
|
||||
max-width: 900px;
|
||||
}
|
||||
img[src*="width=50%"]{
|
||||
max-width: 50%;
|
||||
}
|
||||
|
||||
img[src*="width=33%"]{
|
||||
max-width: 33%;
|
||||
}
|
||||
|
||||
img[src$="width=25%"]{
|
||||
max-width: 25%;
|
||||
}
|
||||
|
||||
#announce{
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
padding: 3px 5px 0;
|
||||
border-top-left-radius: 7px;
|
||||
}
|
||||
|
||||
fieldset.scrolling{
|
||||
overflow: scroll;
|
||||
max-height: 80%;
|
||||
}
|
||||
|
||||
svg#gantt{
|
||||
max-width: unset;
|
||||
}
|
||||
|
||||
svg .row{
|
||||
fill: none;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
svg .row:hover{
|
||||
fill: #333333;
|
||||
}
|
||||
|
||||
svg .duration{
|
||||
fill:rgba(255,0,255,0.4);
|
||||
stroke:none;
|
||||
}
|
||||
|
||||
svg .schedule{
|
||||
fill:none;
|
||||
stroke-width:1;
|
||||
stroke: yellow;
|
||||
}
|
||||
|
||||
svg .start{
|
||||
stroke-dasharray: 10,30,40;
|
||||
}
|
||||
|
||||
svg .stop{
|
||||
stroke-dasharray: 50,30;
|
||||
}
|
||||
|
||||
svg text{
|
||||
stroke: none;
|
||||
fill: red;
|
||||
}
|
||||
|
||||
div.search{
|
||||
display: inline-block;
|
||||
max-width: 150px;
|
||||
}
|
||||
|
||||
div.search input,
|
||||
div.search label{
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.search:hover input,
|
||||
div.search:hover label{
|
||||
display: initial;
|
||||
}
|
||||
|
||||
.note td.code {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.note td.code textarea {
|
||||
min-height: 200px;
|
||||
}
|
||||
|
||||
.project-index td .users{
|
||||
max-height: 30px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.project-index tr:hover td .users{
|
||||
max-height: none;
|
||||
}
|
||||
|
||||
.easylist fieldset a.button {
|
||||
width: 80%;
|
||||
padding: 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#preview,
|
||||
#preview-source{
|
||||
display: inline-block;
|
||||
max-width: calc(50% - 20px);
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
table #preview,
|
||||
table #preview-source{
|
||||
display: inherit;
|
||||
max-width: unset;
|
||||
vertical-align: inherit;
|
||||
}
|
||||
|
||||
#preview.loading{
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.completed > a::before {
|
||||
content: " ✓";
|
||||
}
|
||||
|
||||
@media (max-width:1199px) {
|
||||
*[hide="12"]{
|
||||
display: none;
|
||||
}
|
||||
table.document input{
|
||||
max-width: 100px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1100px){
|
||||
*[hide="11"]{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1000px){
|
||||
*[hide="10"]{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 900px){
|
||||
*[hide="9"]{
|
||||
display: none;
|
||||
}
|
||||
table.document input{
|
||||
max-width: 60px;
|
||||
}
|
||||
table.document .tax input,
|
||||
table.document input.amount{
|
||||
max-width: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 800px){
|
||||
*[hide="8"]{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 700px){
|
||||
*[hide="7"]{
|
||||
display: none;
|
||||
}
|
||||
fieldset.bookmark > fieldset{
|
||||
max-height: 50px;
|
||||
}
|
||||
#preview,
|
||||
#preview-source{
|
||||
display: block;
|
||||
max-width: unset;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px){
|
||||
*[hide="6"]{
|
||||
display: none;
|
||||
}
|
||||
|
||||
.easylist fieldset a.button {
|
||||
width: 60%;
|
||||
}
|
||||
}
|
||||
@media (max-width: 500px){
|
||||
*[hide="5"]{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media (max-width: 400px){
|
||||
*[hide="4"]{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@media print{
|
||||
fieldset.note span.right,
|
||||
#logo, #main_menu {
|
||||
display: none;
|
||||
}
|
||||
fieldset.process, fieldset.database{
|
||||
page-break-after: always;
|
||||
}
|
||||
.export>fieldset{
|
||||
border: 0 none;
|
||||
}
|
||||
}
|
||||
37
legacy/src/main/resources/css/winter/svg_colors.css
Normal file
37
legacy/src/main/resources/css/winter/svg_colors.css
Normal file
@@ -0,0 +1,37 @@
|
||||
.arrow{
|
||||
stroke: blue;
|
||||
}
|
||||
circle, ellipse, rect{
|
||||
stroke: black;
|
||||
}
|
||||
ellipse,
|
||||
circle.process{
|
||||
fill: rgba(255,255,255,0.5);
|
||||
}
|
||||
rect.terminal{
|
||||
fill: white;
|
||||
}
|
||||
circle.connector{
|
||||
fill: rgba(0,0,0,0.05);
|
||||
stroke: none;
|
||||
}
|
||||
circle.connector:hover{
|
||||
fill: rgba(0,0,0,0.6);
|
||||
}
|
||||
|
||||
#backdrop{
|
||||
fill: transparent;
|
||||
}
|
||||
|
||||
.arrow circle{
|
||||
stroke: none;
|
||||
fill: white;
|
||||
}
|
||||
.arrow text{
|
||||
fill: blue;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.arrow:hover circle{
|
||||
fill: rgba(0,0,255,0.3);
|
||||
}
|
||||
BIN
legacy/src/main/resources/edit.cur
Normal file
BIN
legacy/src/main/resources/edit.cur
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
171
legacy/src/main/resources/js/common.js
Normal file
171
legacy/src/main/resources/js/common.js
Normal file
@@ -0,0 +1,171 @@
|
||||
const DEL = 'DELETE';
|
||||
const PATCH = 'PATCH';
|
||||
const POST = 'POST';
|
||||
|
||||
// remove all children from element, leave only the first <keep> children
|
||||
function clearElement(key,keep = 1){
|
||||
var elem = key instanceof Element ? key : get(key);
|
||||
while (elem.childElementCount > keep) elem.removeChild(elem.lastChild);
|
||||
return elem;
|
||||
}
|
||||
|
||||
function create(type,code){
|
||||
var elem = document.createElement(type);
|
||||
if (code) elem.innerHTML = code;
|
||||
return elem;
|
||||
}
|
||||
|
||||
function disable(id){
|
||||
var elem = get(id);
|
||||
elem.setAttribute('disabled','disabled');
|
||||
return elem;
|
||||
}
|
||||
|
||||
function enable(id){
|
||||
var elem = get(id);
|
||||
elem.removeAttribute('disabled');
|
||||
return elem;
|
||||
}
|
||||
|
||||
function get(id){
|
||||
return document.getElementById(id);
|
||||
}
|
||||
|
||||
function getCookie(cname) {
|
||||
let name = cname + "=";
|
||||
let decodedCookie = decodeURIComponent(document.cookie);
|
||||
let arr = decodedCookie.split(';');
|
||||
for(let i = 0; i <arr.length; i++) {
|
||||
let c = arr[i];
|
||||
while (c.charAt(0) == ' ') c = c.substring(1);
|
||||
if (c.indexOf(name) == 0) return c.substring(name.length, c.length);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function getValue(id){
|
||||
return get(id).value;
|
||||
}
|
||||
|
||||
function hide(id){
|
||||
var elem = get(id);
|
||||
if (elem) elem.style.display = 'none';
|
||||
return elem;
|
||||
}
|
||||
|
||||
function hideAll(clazz){
|
||||
var elems = document.getElementsByTagName('*'), i;
|
||||
for (i in elems) {
|
||||
if((' ' + elems[i].className + ' ').indexOf(' ' + clazz + ' ') > -1) elems[i].style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
function isChecked(id){
|
||||
return get(id).checked;
|
||||
}
|
||||
|
||||
function menu(){
|
||||
fetch('<? user.api.menu ?>').then(showMenu);
|
||||
}
|
||||
|
||||
// Replacement for Object.toEntries(…)
|
||||
function paramsToObject(entries) {
|
||||
const result = {};
|
||||
for(var key of entries) { // each 'entry' is a [key, value] tupple
|
||||
result[key[0]] = key[1];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function redirect(page){
|
||||
window.location.href = page;
|
||||
}
|
||||
|
||||
function setText(id, text){
|
||||
var elem = get(id);
|
||||
elem.innerHTML = text;
|
||||
return elem;
|
||||
}
|
||||
|
||||
function setValue(id,newVal){
|
||||
var elem = get(id);
|
||||
if (elem) elem.value = newVal;
|
||||
return elem;
|
||||
}
|
||||
|
||||
function show(id){
|
||||
var elem = get(id);
|
||||
if (elem) elem.style.display = '';
|
||||
return elem;
|
||||
}
|
||||
|
||||
function showAll(clazz){
|
||||
var elems = document.getElementsByTagName('*'), i;
|
||||
for (i in elems) {
|
||||
if((' ' + elems[i].className + ' ').indexOf(' ' + clazz + ' ') > -1) elems[i].style.display = '';
|
||||
}
|
||||
}
|
||||
|
||||
function showError(json){
|
||||
var message = json.message;
|
||||
var box = show('error');
|
||||
box.innerHTML = message;
|
||||
|
||||
var button = document.createElement('button');
|
||||
button.innerText = 'ok';
|
||||
button.addEventListener('click',() => hide('error'))
|
||||
box.appendChild(button);
|
||||
box.scrollIntoView();
|
||||
}
|
||||
|
||||
async function showMenu(resp){
|
||||
if (resp.ok){
|
||||
var menu = get('main_menu');
|
||||
var json = await resp.json();
|
||||
var entries = json.entries;
|
||||
entries.sort((a,b) => a.index - b.index);
|
||||
for (var idx in entries){
|
||||
var entry = entries[idx];
|
||||
menu.insertAdjacentHTML('beforeend','<a class="button" href="'+entry.url+'">'+entry.caption+'</a>');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function sortTableColumn(column){
|
||||
var table = column;
|
||||
var columnIndex = [...column.parentNode.children].indexOf(column);
|
||||
while (table.localName != 'table'){
|
||||
if (!table.parentNode) return;
|
||||
table = table.parentNode;
|
||||
}
|
||||
var rows = Array.from(table.tBodies[0].rows);
|
||||
var asc = table.getAttribute("data-sort-dir") !== "desc";
|
||||
table.setAttribute("data-sort-dir", asc ? "desc" : "asc");
|
||||
rows.sort(function(a, b) {
|
||||
var cellA = a.cells[columnIndex].innerText.trim();
|
||||
var cellB = b.cells[columnIndex].innerText.trim();
|
||||
var numA = parseFloat(cellA);
|
||||
var numB = parseFloat(cellB);
|
||||
if (!isNaN(numA) && !isNaN(numB)) return asc ? numA - numB : numB - numA;
|
||||
return asc ? cellA.localeCompare(cellB) : cellB.localeCompare(cellA);
|
||||
});
|
||||
|
||||
// Append sorted rows back to the tbody
|
||||
rows.forEach(function(row) {
|
||||
table.tBodies[0].appendChild(row);
|
||||
});
|
||||
}
|
||||
|
||||
function spread(json,prefix){
|
||||
for (var key in json){
|
||||
var val = json[key];
|
||||
var id = prefix+"."+key;
|
||||
if (typeof val === 'object'){
|
||||
spread(val,id);
|
||||
continue;
|
||||
}
|
||||
if (typeof val === 'string') val = val.replaceAll("\n","<br/>\n");
|
||||
var tag = get(id);
|
||||
if (tag) tag.innerHTML = val;
|
||||
}
|
||||
}
|
||||
4
legacy/src/main/resources/js/jquery-3.2.1.min.js
vendored
Normal file
4
legacy/src/main/resources/js/jquery-3.2.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
124
legacy/src/main/resources/js/umbrella.js
Normal file
124
legacy/src/main/resources/js/umbrella.js
Normal file
@@ -0,0 +1,124 @@
|
||||
const POST = 'POST';
|
||||
|
||||
function toggle(selector){
|
||||
$(selector).toggle("slow");
|
||||
}
|
||||
|
||||
function addDescriptionOption(text){
|
||||
if (text.trim() == '') return;
|
||||
var select = $('select[name=alt_comment]');
|
||||
if (select.length == 0){
|
||||
var area=$('textarea[name=comment]');
|
||||
var hint= area.attr('descr');
|
||||
$('<select/>',{name: 'alt_comment'})
|
||||
.append($('<option/>',{value: '',text: hint}))
|
||||
.insertBefore(area);
|
||||
select = $('select[name=alt_comment]');
|
||||
select.on('change',function(){
|
||||
$('textarea[name=comment]').val(select.find('option:selected').text());
|
||||
});
|
||||
}
|
||||
select.append($('<option/>',{text: text}));
|
||||
}
|
||||
|
||||
function getHeadings(elem){
|
||||
$('select[name=alt_comment]').remove();
|
||||
$('textarea[name=comment]').val('');
|
||||
var url=window.location.href.replace(/\/([^\/]*)$/,'/headings')+'?page='+encodeURIComponent(elem.value);
|
||||
$.ajax({
|
||||
url: url,
|
||||
dataType: "json",
|
||||
success: function(data){
|
||||
for (var index in data.headings) addDescriptionOption(data.headings[index]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var preview_timer = 0;
|
||||
|
||||
function keyEvent(e){
|
||||
if (e.ctrlKey){
|
||||
if (e.key === 'f') { // display search form on Ctrl+F
|
||||
e.preventDefault();
|
||||
$('.search *').show();
|
||||
$('.search form>input').focus();
|
||||
} else
|
||||
if (e.key === 's') { // save current element on Ctrl+S
|
||||
var saveBtn = $('form[method=POST] button[type="submit"]')[0];
|
||||
if (saveBtn) {
|
||||
e.preventDefault();
|
||||
saveBtn.click();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (e.altKey){
|
||||
switch (e.keyCode){
|
||||
case 38:
|
||||
$('a.parent').each(function(){this.click()});
|
||||
return;
|
||||
case 39:
|
||||
$('a.next').each(function(){this.click()});
|
||||
return
|
||||
default:
|
||||
console.log(e.keyCode);
|
||||
}
|
||||
}
|
||||
switch (e.keyCode){
|
||||
case 27: // ESC
|
||||
if (document.location.href.includes("/add")) window.history.back();
|
||||
if (document.location.href.includes("/edit")) window.history.back();
|
||||
return;
|
||||
case 107: // +
|
||||
var active = document.activeElement.tagName;
|
||||
switch (active){
|
||||
case 'TEXTAREA':
|
||||
// do not activate add-link when in these fields
|
||||
return;
|
||||
}
|
||||
var addLink = $('a[href^="add"]')[0];
|
||||
if (addLink) addLink.click();
|
||||
return;
|
||||
}
|
||||
if (e.target.id == 'preview-source') {
|
||||
clearTimeout(preview_timer);
|
||||
preview_timer = setTimeout(preview,750,e.target);
|
||||
}
|
||||
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
getHeadingsTimer = null;
|
||||
|
||||
function getHeadings_delayed(){
|
||||
if (getHeadingsTimer != null) clearTimeout(getHeadingsTimer);
|
||||
getHeadingsTimer = window.setTimeout(getHeadings,200,this);
|
||||
}
|
||||
|
||||
async function handlePreview(resp,txt){
|
||||
const target = document.getElementById('preview');
|
||||
if (resp.ok){
|
||||
const content = await resp.text();
|
||||
$(target).removeClass('loading').html(content);
|
||||
setTimeout(() => {
|
||||
$(txt).css('height',Math.max(target.clientHeight,200));
|
||||
},200);
|
||||
} else {
|
||||
$(target).removeClass('loading').html('Preview failed…');
|
||||
console.log("preview request failed!");
|
||||
}
|
||||
}
|
||||
|
||||
function preview(txt){
|
||||
let target = document.getElementById('preview');
|
||||
if (!target) return;
|
||||
$(target).addClass('loading');
|
||||
$(target).html('loading…');
|
||||
let url = '<? base ?>/api/preview';
|
||||
fetch(url,{
|
||||
method: POST,
|
||||
body: txt.value,
|
||||
credentials: 'include'
|
||||
}).then(resp => handlePreview(resp,txt));
|
||||
}
|
||||
document.addEventListener('keydown',keyEvent);
|
||||
BIN
legacy/src/main/resources/umbrella100px.png
Normal file
BIN
legacy/src/main/resources/umbrella100px.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.4 KiB |
@@ -2,6 +2,7 @@ description = "Umbrella : User"
|
||||
|
||||
dependencies{
|
||||
implementation(project(":core"))
|
||||
implementation("de.srsoftware:configuration.api:1.0.2")
|
||||
implementation("de.srsoftware:tools.jdbc:1.3.2")
|
||||
implementation("de.srsoftware:tools.mime:1.1.2")
|
||||
implementation("de.srsoftware:tools.optionals:1.0.0")
|
||||
|
||||
@@ -35,8 +35,6 @@ public class Constants {
|
||||
|
||||
public static final String PASS = "pass";
|
||||
|
||||
|
||||
|
||||
public static final String REDIRECT_URI = "redirect_uri";
|
||||
public static final String RESPONSE_TYPE = "response_type";
|
||||
public static final String SCOPE = "scope";
|
||||
|
||||
@@ -23,7 +23,6 @@ import static java.time.temporal.ChronoUnit.DAYS;
|
||||
|
||||
import com.sun.net.httpserver.HttpExchange;
|
||||
import de.srsoftware.tools.Path;
|
||||
import de.srsoftware.tools.PathHandler;
|
||||
import de.srsoftware.tools.SessionToken;
|
||||
import de.srsoftware.umbrella.core.BaseHandler;
|
||||
import de.srsoftware.umbrella.core.Token;
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/* © SRSoftware 2025 */
|
||||
package de.srsoftware.umbrella.user.api;
|
||||
|
||||
import de.srsoftware.umbrella.core.Token;
|
||||
import de.srsoftware.umbrella.core.UmbrellaException;
|
||||
import de.srsoftware.umbrella.user.model.DbUser;
|
||||
import de.srsoftware.umbrella.user.model.Password;
|
||||
import de.srsoftware.umbrella.user.model.Session;
|
||||
import de.srsoftware.umbrella.core.Token;
|
||||
import de.srsoftware.umbrella.user.model.UmbrellaUser;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ package de.srsoftware.umbrella.user.model;
|
||||
|
||||
import de.srsoftware.tools.SessionToken;
|
||||
import de.srsoftware.umbrella.core.Token;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/* © SRSoftware 2025 */
|
||||
|
||||
@@ -11,12 +11,12 @@ import static java.lang.System.Logger.Level.*;
|
||||
import static java.text.MessageFormat.format;
|
||||
|
||||
import de.srsoftware.tools.jdbc.Query;
|
||||
import de.srsoftware.umbrella.core.Token;
|
||||
import de.srsoftware.umbrella.core.UmbrellaException;
|
||||
import de.srsoftware.umbrella.user.api.LoginServiceDb;
|
||||
import de.srsoftware.umbrella.user.api.UserDb;
|
||||
import de.srsoftware.umbrella.user.model.*;
|
||||
import de.srsoftware.umbrella.user.model.Session;
|
||||
import de.srsoftware.umbrella.core.Token;
|
||||
import de.srsoftware.umbrella.user.model.UmbrellaUser;
|
||||
import java.sql.Connection;
|
||||
import java.sql.ResultSet;
|
||||
|
||||
Reference in New Issue
Block a user