Compare commits
54 Commits
muwire-0.4
...
muwire-0.4
Author | SHA1 | Date | |
---|---|---|---|
![]() |
abe28517bc | ||
![]() |
15bc4c064d | ||
![]() |
91d771944b | ||
![]() |
e09c456a13 | ||
![]() |
d9c1067226 | ||
![]() |
eda3e7ad3a | ||
![]() |
e9798c7eaa | ||
![]() |
66bb4eef5b | ||
![]() |
55f260b3f4 | ||
![]() |
32d4c3965e | ||
![]() |
de1534d837 | ||
![]() |
7b58e8a88a | ||
![]() |
8a03b89985 | ||
![]() |
1d97374857 | ||
![]() |
549e8c2d98 | ||
![]() |
b54d24db0d | ||
![]() |
fa12e84345 | ||
![]() |
6430ff2691 | ||
![]() |
591313c81c | ||
![]() |
ce7b6a0c65 | ||
![]() |
5c4d4c4580 | ||
![]() |
4cb864ff9f | ||
![]() |
417675ad07 | ||
![]() |
9513e5ba3c | ||
![]() |
85610cf169 | ||
![]() |
e8322384b8 | ||
![]() |
179279ed30 | ||
![]() |
ae79f0fded | ||
![]() |
ed878b3762 | ||
![]() |
623cca0ef2 | ||
![]() |
eaa883c3ba | ||
![]() |
7ae8076865 | ||
![]() |
b1aa92661c | ||
![]() |
9ed94c8376 | ||
![]() |
fa6aea1abe | ||
![]() |
0de84e704b | ||
![]() |
a767dda044 | ||
![]() |
56e9235d7b | ||
![]() |
2fba9a74ce | ||
![]() |
2bb6826906 | ||
![]() |
9f339629a9 | ||
![]() |
58d4207f94 | ||
![]() |
32577a28dc | ||
![]() |
f7b43304d4 | ||
![]() |
dcbe09886d | ||
![]() |
5a54b2dcda | ||
![]() |
581293b24f | ||
![]() |
cd072b9f76 | ||
![]() |
6b74fc5956 | ||
![]() |
3de2f872bb | ||
![]() |
fcde917d08 | ||
![]() |
4ded065010 | ||
![]() |
18a1c7091a | ||
![]() |
46aee19f80 |
16
README.md
@@ -4,14 +4,14 @@ MuWire is an easy to use file-sharing program which offers anonymity using [I2P
|
||||
|
||||
It is inspired by the LimeWire Gnutella client and developped by a former LimeWire developer.
|
||||
|
||||
The current stable release - 0.4.6 is avaiable for download at https://muwire.com. You can find technical documentation in the "doc" folder.
|
||||
The current stable release - 0.4.13 is avaiable for download at https://muwire.com. You can find technical documentation in the "doc" folder.
|
||||
|
||||
### Building
|
||||
|
||||
You need JRE 8 or newer. After installing that and setting up the appropriate paths, just type
|
||||
|
||||
```
|
||||
./gradlew clean assemble
|
||||
./gradlew clean assemble
|
||||
```
|
||||
|
||||
If you want to run the unit tests, type
|
||||
@@ -21,15 +21,23 @@ If you want to run the unit tests, type
|
||||
|
||||
Some of the UI tests will fail because they haven't been written yet :-/
|
||||
|
||||
If you want to build binary bundles for Windows and Mac that do not depend on Java or I2P, see the https://github.com/zlatinb/muwire-pkg project
|
||||
|
||||
### Running
|
||||
|
||||
After you build the application, look inside `gui/build/distributions`. Untar/unzip one of the `shadow` files and then run the jar contained inside by typing `java -jar MuWire-x.y.z.jar` in a terminal or command prompt.
|
||||
After you build the application, look inside `gui/build/distributions`. Untar/unzip one of the `shadow` files and then run the jar contained inside by typing `java -jar MuWire-x.y.z.jar` in a terminal or command prompt.
|
||||
|
||||
If you have an I2P router running on the same machine that is all you need to do. If you use a custom I2CP host and port, create a file `i2p.properties` and put `i2cp.tcp.host=<host>` and `i2cp.tcp.port=<port>` in there. On Windows that file should go into `%HOME%\AppData\Roaming\MuWire`, on Mac into `$HOME/Library/Application Support/MuWire` and on Linux `$HOME/.MuWire`
|
||||
|
||||
If you do not have an I2P router, pass the following switch to the Java process: `-DembeddedRouter=true`. This will launch MuWire's embedded router. Be aware that this causes startup to take a lot longer.
|
||||
[Default I2CP port]\: `7654`
|
||||
|
||||
### GPG Fingerprint
|
||||
|
||||
```
|
||||
471B 9FD4 5517 A5ED 101F C57D A728 3207 2D52 5E41
|
||||
```
|
||||
|
||||
You can find the full key at https://keybase.io/zlatinb
|
||||
|
||||
|
||||
[Default I2CP port]: https://geti2p.net/en/docs/ports
|
||||
|
5
TODO.md
@@ -12,10 +12,6 @@ This reduces query traffic by not sending last hop queries to peers that definit
|
||||
|
||||
This helps with scalability
|
||||
|
||||
##### Content Control Panel
|
||||
|
||||
To allow every user to not route queries for content they do not like. This is mostly GUI work, the backend part is simple
|
||||
|
||||
##### Web UI, REST Interface, etc.
|
||||
|
||||
Basically any non-gui non-cli user interface
|
||||
@@ -29,3 +25,4 @@ To enable parsing of metadata from known file types and the user editing it or a
|
||||
* Wrapper of some kind for in-place upgrades
|
||||
* Download file sequentially
|
||||
* Multiple-selection download, Ctrl-A
|
||||
* Automatic adjustment of number of I2P tunnels
|
||||
|
@@ -2,7 +2,7 @@ subprojects {
|
||||
apply plugin: 'groovy'
|
||||
|
||||
dependencies {
|
||||
compile 'net.i2p:i2p:0.9.41'
|
||||
compile 'net.i2p:i2p:0.9.42'
|
||||
compile 'org.codehaus.groovy:groovy-all:2.4.15'
|
||||
}
|
||||
|
||||
|
@@ -35,7 +35,7 @@ class Cli {
|
||||
|
||||
Core core
|
||||
try {
|
||||
core = new Core(props, home, "0.4.9")
|
||||
core = new Core(props, home, "0.4.14")
|
||||
} catch (Exception bad) {
|
||||
bad.printStackTrace(System.out)
|
||||
println "Failed to initialize core, exiting"
|
||||
|
@@ -53,7 +53,7 @@ class CliDownloader {
|
||||
|
||||
Core core
|
||||
try {
|
||||
core = new Core(props, home, "0.4.9")
|
||||
core = new Core(props, home, "0.4.14")
|
||||
} catch (Exception bad) {
|
||||
bad.printStackTrace(System.out)
|
||||
println "Failed to initialize core, exiting"
|
||||
|
@@ -2,9 +2,9 @@ apply plugin : 'application'
|
||||
mainClassName = 'com.muwire.core.Core'
|
||||
applicationDefaultJvmArgs = ['-Djava.util.logging.config.file=logging.properties']
|
||||
dependencies {
|
||||
compile 'net.i2p:router:0.9.41'
|
||||
compile 'net.i2p.client:mstreaming:0.9.41'
|
||||
compile 'net.i2p.client:streaming:0.9.41'
|
||||
compile 'net.i2p:router:0.9.42'
|
||||
compile 'net.i2p.client:mstreaming:0.9.42'
|
||||
compile 'net.i2p.client:streaming:0.9.42'
|
||||
|
||||
testCompile 'org.junit.jupiter:junit-jupiter-api:5.4.2'
|
||||
testCompile 'junit:junit:4.12'
|
||||
|
@@ -220,7 +220,7 @@ public class Core {
|
||||
eventBus.register(SourceDiscoveredEvent.class, meshManager)
|
||||
|
||||
log.info "initializing persistence service"
|
||||
persisterService = new PersisterService(new File(home, "files.json"), eventBus, 15000, fileManager)
|
||||
persisterService = new PersisterService(new File(home, "files.json"), eventBus, 60000, fileManager)
|
||||
eventBus.register(UILoadedEvent.class, persisterService)
|
||||
|
||||
log.info("initializing host cache")
|
||||
@@ -361,7 +361,7 @@ public class Core {
|
||||
}
|
||||
}
|
||||
|
||||
Core core = new Core(props, home, "0.4.9")
|
||||
Core core = new Core(props, home, "0.4.14")
|
||||
core.startServices()
|
||||
|
||||
// ... at the end, sleep or execute script
|
||||
|
@@ -11,6 +11,7 @@ class MuWireSettings {
|
||||
|
||||
final boolean isLeaf
|
||||
boolean allowUntrusted
|
||||
boolean searchExtraHop
|
||||
boolean allowTrustLists
|
||||
int trustListInterval
|
||||
Set<Persona> trustSubscriptions
|
||||
@@ -24,7 +25,7 @@ class MuWireSettings {
|
||||
boolean shareDownloadedFiles
|
||||
Set<String> watchedDirectories
|
||||
float downloadSequentialRatio
|
||||
int hostClearInterval
|
||||
int hostClearInterval, hostHopelessInterval, hostRejectInterval
|
||||
int meshExpiration
|
||||
boolean embeddedRouter
|
||||
int inBw, outBw
|
||||
@@ -38,19 +39,22 @@ class MuWireSettings {
|
||||
MuWireSettings(Properties props) {
|
||||
isLeaf = Boolean.valueOf(props.get("leaf","false"))
|
||||
allowUntrusted = Boolean.valueOf(props.getProperty("allowUntrusted","true"))
|
||||
searchExtraHop = Boolean.valueOf(props.getProperty("searchExtraHop","false"))
|
||||
allowTrustLists = Boolean.valueOf(props.getProperty("allowTrustLists","true"))
|
||||
trustListInterval = Integer.valueOf(props.getProperty("trustListInterval","1"))
|
||||
crawlerResponse = CrawlerResponse.valueOf(props.get("crawlerResponse","REGISTERED"))
|
||||
nickname = props.getProperty("nickname","MuWireUser")
|
||||
downloadLocation = new File((String)props.getProperty("downloadLocation",
|
||||
System.getProperty("user.home")))
|
||||
downloadRetryInterval = Integer.parseInt(props.getProperty("downloadRetryInterval","1"))
|
||||
downloadRetryInterval = Integer.parseInt(props.getProperty("downloadRetryInterval","60"))
|
||||
updateCheckInterval = Integer.parseInt(props.getProperty("updateCheckInterval","24"))
|
||||
autoDownloadUpdate = Boolean.parseBoolean(props.getProperty("autoDownloadUpdate","true"))
|
||||
updateType = props.getProperty("updateType","jar")
|
||||
shareDownloadedFiles = Boolean.parseBoolean(props.getProperty("shareDownloadedFiles","true"))
|
||||
downloadSequentialRatio = Float.valueOf(props.getProperty("downloadSequentialRatio","0.8"))
|
||||
hostClearInterval = Integer.valueOf(props.getProperty("hostClearInterval","60"))
|
||||
hostClearInterval = Integer.valueOf(props.getProperty("hostClearInterval","15"))
|
||||
hostHopelessInterval = Integer.valueOf(props.getProperty("hostHopelessInterval", "1440"))
|
||||
hostRejectInterval = Integer.valueOf(props.getProperty("hostRejectInterval", "1"))
|
||||
meshExpiration = Integer.valueOf(props.getProperty("meshExpiration","60"))
|
||||
embeddedRouter = Boolean.valueOf(props.getProperty("embeddedRouter","false"))
|
||||
inBw = Integer.valueOf(props.getProperty("inBw","256"))
|
||||
@@ -74,6 +78,7 @@ class MuWireSettings {
|
||||
Properties props = new Properties()
|
||||
props.setProperty("leaf", isLeaf.toString())
|
||||
props.setProperty("allowUntrusted", allowUntrusted.toString())
|
||||
props.setProperty("searchExtraHop", String.valueOf(searchExtraHop))
|
||||
props.setProperty("allowTrustLists", String.valueOf(allowTrustLists))
|
||||
props.setProperty("trustListInterval", String.valueOf(trustListInterval))
|
||||
props.setProperty("crawlerResponse", crawlerResponse.toString())
|
||||
@@ -86,6 +91,8 @@ class MuWireSettings {
|
||||
props.setProperty("shareDownloadedFiles", String.valueOf(shareDownloadedFiles))
|
||||
props.setProperty("downloadSequentialRatio", String.valueOf(downloadSequentialRatio))
|
||||
props.setProperty("hostClearInterval", String.valueOf(hostClearInterval))
|
||||
props.setProperty("hostHopelessInterval", String.valueOf(hostHopelessInterval))
|
||||
props.setProperty("hostRejectInterval", String.valueOf(hostRejectInterval))
|
||||
props.setProperty("meshExpiration", String.valueOf(meshExpiration))
|
||||
props.setProperty("embeddedRouter", String.valueOf(embeddedRouter))
|
||||
props.setProperty("inBw", String.valueOf(inBw))
|
||||
|
@@ -31,7 +31,7 @@ class ConnectionEstablisher {
|
||||
final HostCache hostCache
|
||||
|
||||
final Timer timer
|
||||
final ExecutorService executor
|
||||
final ExecutorService executor, closer
|
||||
|
||||
final Set inProgress = new ConcurrentHashSet()
|
||||
|
||||
@@ -51,6 +51,8 @@ class ConnectionEstablisher {
|
||||
rv.setName("connector-${System.currentTimeMillis()}")
|
||||
rv
|
||||
} as ThreadFactory)
|
||||
|
||||
closer = Executors.newSingleThreadExecutor()
|
||||
}
|
||||
|
||||
void start() {
|
||||
@@ -60,6 +62,7 @@ class ConnectionEstablisher {
|
||||
void stop() {
|
||||
timer.cancel()
|
||||
executor.shutdownNow()
|
||||
closer.shutdown()
|
||||
}
|
||||
|
||||
private void connectIfNeeded() {
|
||||
@@ -120,8 +123,10 @@ class ConnectionEstablisher {
|
||||
}
|
||||
|
||||
private void fail(Endpoint endpoint) {
|
||||
endpoint.close()
|
||||
eventBus.publish(new ConnectionEvent(endpoint: endpoint, incoming: false, leaf: false, status: ConnectionAttemptStatus.FAILED))
|
||||
closer.execute {
|
||||
endpoint.close()
|
||||
eventBus.publish(new ConnectionEvent(endpoint: endpoint, incoming: false, leaf: false, status: ConnectionAttemptStatus.FAILED))
|
||||
} as Runnable
|
||||
}
|
||||
|
||||
private void readK(Endpoint e) {
|
||||
@@ -175,7 +180,7 @@ class ConnectionEstablisher {
|
||||
log.log(Level.WARNING,"Problem parsing post-rejection payload",ignore)
|
||||
} finally {
|
||||
// the end
|
||||
e.close()
|
||||
closer.execute({e.close()} as Runnable)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -74,7 +74,7 @@ public class DownloadManager {
|
||||
destinations.addAll(e.sources)
|
||||
destinations.remove(me.destination)
|
||||
|
||||
Pieces pieces = getPieces(infohash, size, pieceSize)
|
||||
Pieces pieces = getPieces(infohash, size, pieceSize, e.sequential)
|
||||
|
||||
def downloader = new Downloader(eventBus, this, me, e.target, size,
|
||||
infohash, pieceSize, connector, destinations,
|
||||
@@ -122,8 +122,12 @@ public class DownloadManager {
|
||||
byte [] root = Base64.decode(json.hashRoot)
|
||||
infoHash = new InfoHash(root)
|
||||
}
|
||||
|
||||
boolean sequential = false
|
||||
if (json.sequential != null)
|
||||
sequential = json.sequential
|
||||
|
||||
Pieces pieces = getPieces(infoHash, (long)json.length, json.pieceSizePow2)
|
||||
Pieces pieces = getPieces(infoHash, (long)json.length, json.pieceSizePow2, sequential)
|
||||
|
||||
def downloader = new Downloader(eventBus, this, me, file, (long)json.length,
|
||||
infoHash, json.pieceSizePow2, connector, destinations, incompletes, pieces)
|
||||
@@ -137,12 +141,12 @@ public class DownloadManager {
|
||||
}
|
||||
}
|
||||
|
||||
private Pieces getPieces(InfoHash infoHash, long length, int pieceSizePow2) {
|
||||
private Pieces getPieces(InfoHash infoHash, long length, int pieceSizePow2, boolean sequential) {
|
||||
int pieceSize = 0x1 << pieceSizePow2
|
||||
int nPieces = (int)(length / pieceSize)
|
||||
if (length % pieceSize != 0)
|
||||
nPieces++
|
||||
Mesh mesh = meshManager.getOrCreate(infoHash, nPieces)
|
||||
Mesh mesh = meshManager.getOrCreate(infoHash, nPieces, sequential)
|
||||
mesh.pieces
|
||||
}
|
||||
|
||||
@@ -188,6 +192,9 @@ public class DownloadManager {
|
||||
json.hashRoot = Base64.encode(infoHash.getRoot())
|
||||
|
||||
json.paused = downloader.paused
|
||||
|
||||
json.sequential = downloader.pieces.ratio == 0f
|
||||
|
||||
writer.println(JsonOutput.toJson(json))
|
||||
}
|
||||
}
|
||||
|
@@ -326,7 +326,7 @@ public class Downloader {
|
||||
}
|
||||
eventBus.publish(
|
||||
new FileDownloadedEvent(
|
||||
downloadedFile : new DownloadedFile(file, getInfoHash(), pieceSizePow2, successfulDestinations),
|
||||
downloadedFile : new DownloadedFile(file.getCanonicalFile(), getInfoHash(), pieceSizePow2, successfulDestinations),
|
||||
downloader : Downloader.this))
|
||||
|
||||
}
|
||||
|
@@ -17,7 +17,7 @@ class Pieces {
|
||||
done = new BitSet(nPieces)
|
||||
claimed = new BitSet(nPieces)
|
||||
}
|
||||
|
||||
|
||||
synchronized int[] claim() {
|
||||
int claimedCardinality = claimed.cardinality()
|
||||
if (claimedCardinality == nPieces) {
|
||||
@@ -30,7 +30,7 @@ class Pieces {
|
||||
}
|
||||
|
||||
// if fuller than ratio just do sequential
|
||||
if ( (1.0f * claimedCardinality) / nPieces > ratio) {
|
||||
if ( (1.0f * claimedCardinality) / nPieces >= ratio) {
|
||||
int rv = claimed.nextClearBit(0)
|
||||
claimed.set(rv)
|
||||
return [rv, partials.getOrDefault(rv, 0), 0]
|
||||
@@ -59,7 +59,8 @@ class Pieces {
|
||||
return [rv, partials.getOrDefault(rv, 0), 1]
|
||||
}
|
||||
List<Integer> toList = availableCopy.toList()
|
||||
Collections.shuffle(toList)
|
||||
if (ratio > 0f)
|
||||
Collections.shuffle(toList)
|
||||
int rv = toList[0]
|
||||
claimed.set(rv)
|
||||
[rv, partials.getOrDefault(rv, 0), 0]
|
||||
|
@@ -10,4 +10,5 @@ class UIDownloadEvent extends Event {
|
||||
UIResultEvent[] result
|
||||
Set<Destination> sources
|
||||
File target
|
||||
boolean sequential
|
||||
}
|
||||
|
@@ -136,8 +136,8 @@ class PersisterService extends Service {
|
||||
|
||||
private def toJson(File f, SharedFile sf) {
|
||||
def json = [:]
|
||||
json.file = Base64.encode DataUtil.encodei18nString(f.getCanonicalFile().toString())
|
||||
json.length = f.length()
|
||||
json.file = Base64.encode DataUtil.encodei18nString(f.toString())
|
||||
json.length = sf.getCachedLength()
|
||||
InfoHash ih = sf.getInfoHash()
|
||||
json.infoHash = Base64.encode ih.getRoot()
|
||||
json.pieceSize = sf.getPieceSize()
|
||||
|
@@ -6,8 +6,12 @@ class CacheServers {
|
||||
|
||||
private static final int TO_GIVE = 3
|
||||
private static Set<Destination> CACHES = [
|
||||
// zlatinb
|
||||
new Destination("Wddh2E6FyyXBF7SvUYHKdN-vjf3~N6uqQWNeBDTM0P33YjiQCOsyedrjmDZmWFrXUJfJLWnCb5bnKezfk4uDaMyj~uvDG~yvLVcFgcPWSUd7BfGgym-zqcG1q1DcM8vfun-US7YamBlmtC6MZ2j-~Igqzmgshita8aLPCfNAA6S6e2UMjjtG7QIXlxpMec75dkHdJlVWbzrk9z8Qgru3YIk0UztYgEwDNBbm9wInsbHhr3HtAfa02QcgRVqRN2PnQXuqUJs7R7~09FZPEviiIcUpkY3FeyLlX1sgQFBeGeA96blaPvZNGd6KnNdgfLgMebx5SSxC-N4KZMSMBz5cgonQF3~m2HHFRSI85zqZNG5X9bJN85t80ltiv1W1es8ZnQW4es11r7MrvJNXz5bmSH641yJIvS6qI8OJJNpFVBIQSXLD-96TayrLQPaYw~uNZ-eXaE6G5dYhiuN8xHsFI1QkdaUaVZnvDGfsRbpS5GtpUbBDbyLkdPurG0i7dN1wAAAA"),
|
||||
new Destination("JC63wJNOqSJmymkj4~UJWywBTvDGikKMoYP0HX2Wz9c5l3otXSkwnxWAFL4cKr~Ygh3BNNi2t93vuLIiI1W8AsE42kR~PwRx~Y-WvIHXR6KUejRmOp-n8WidtjKg9k4aDy428uSOedqXDxys5mpoeQXwDsv1CoPTTwnmb1GWFy~oTGIsCguCl~aJWGnqiKarPO3GJQ~ev-NbvAQzUfC3HeP1e6pdI5CGGjExahTCID5UjpJw8GaDXWlGmYWWH303Xu4x-vAHQy1dJLsOBCn8dZravsn5BKJk~j0POUon45CCx-~NYtaPe0Itt9cMdD2ciC76Rep1D0X0sm1SjlSs8sZ52KmF3oaLZ6OzgI9QLMIyBUrfi41sK5I0qTuUVBAkvW1xr~L-20dYJ9TrbOaOb2-vDIfKaxVi6xQOuhgQDiSBhd3qv2m0xGu-BM9DQYfNA0FdMjnZmqjmji9RMavzQSsVFIbQGLbrLepiEFlb7TseCK5UtRp8TxnG7L4gbYevBQAEAAcAAA==")
|
||||
// sNL
|
||||
new Destination("JC63wJNOqSJmymkj4~UJWywBTvDGikKMoYP0HX2Wz9c5l3otXSkwnxWAFL4cKr~Ygh3BNNi2t93vuLIiI1W8AsE42kR~PwRx~Y-WvIHXR6KUejRmOp-n8WidtjKg9k4aDy428uSOedqXDxys5mpoeQXwDsv1CoPTTwnmb1GWFy~oTGIsCguCl~aJWGnqiKarPO3GJQ~ev-NbvAQzUfC3HeP1e6pdI5CGGjExahTCID5UjpJw8GaDXWlGmYWWH303Xu4x-vAHQy1dJLsOBCn8dZravsn5BKJk~j0POUon45CCx-~NYtaPe0Itt9cMdD2ciC76Rep1D0X0sm1SjlSs8sZ52KmF3oaLZ6OzgI9QLMIyBUrfi41sK5I0qTuUVBAkvW1xr~L-20dYJ9TrbOaOb2-vDIfKaxVi6xQOuhgQDiSBhd3qv2m0xGu-BM9DQYfNA0FdMjnZmqjmji9RMavzQSsVFIbQGLbrLepiEFlb7TseCK5UtRp8TxnG7L4gbYevBQAEAAcAAA=="),
|
||||
// dark_trion
|
||||
new Destination("Gec9L29FVcQvYDgpcYuEYdltJn06PPoOWAcAM8Af-gDm~ehlrJcwlLXXs0hidq~yP2A0X7QcDi6i6shAfuEofTchxGJl8LRNqj9lio7WnB7cIixXWL~uCkD7Np5LMX0~akNX34oOb9RcBYVT2U5rFGJmJ7OtBv~IBkGeLhsMrqaCjahd0jdBO~QJ-t82ZKZhh044d24~JEfF9zSJxdBoCdAcXzryGNy7sYtFVDFsPKJudAxSW-UsSQiGw2~k-TxyF0r-iAt1IdzfNu8Lu0WPqLdhDYJWcPldx2PR5uJorI~zo~z3I5RX3NwzarlbD4nEP5s65ahPSfVCEkzmaJUBgP8DvBqlFaX89K4nGRYc7jkEjJ8cX4L6YPXUpTPWcfKkW259WdQY3YFh6x7rzijrGZewpczOLCrt-bZRYgDrUibmZxKZmNhy~lQu4gYVVjkz1i4tL~DWlhIc4y0x2vItwkYLArPPi~ejTnt-~Lhb7oPMXRcWa3UrwGKpFvGZY4NXBQAEAAcAAA==")
|
||||
]
|
||||
|
||||
static List<Destination> getCacheServers() {
|
||||
|
@@ -7,21 +7,35 @@ class Host {
|
||||
private static final int MAX_FAILURES = 3
|
||||
|
||||
final Destination destination
|
||||
private final int clearInterval
|
||||
private final int clearInterval, hopelessInterval, rejectionInterval
|
||||
int failures,successes
|
||||
long lastAttempt
|
||||
long lastSuccessfulAttempt
|
||||
long lastRejection
|
||||
|
||||
public Host(Destination destination, int clearInterval) {
|
||||
public Host(Destination destination, int clearInterval, int hopelessInterval, int rejectionInterval) {
|
||||
this.destination = destination
|
||||
this.clearInterval = clearInterval
|
||||
this.hopelessInterval = hopelessInterval
|
||||
this.rejectionInterval = rejectionInterval
|
||||
}
|
||||
|
||||
synchronized void onConnect() {
|
||||
|
||||
private void connectSuccessful() {
|
||||
failures = 0
|
||||
successes++
|
||||
lastAttempt = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
synchronized void onConnect() {
|
||||
connectSuccessful()
|
||||
lastSuccessfulAttempt = lastAttempt
|
||||
}
|
||||
|
||||
synchronized void onReject() {
|
||||
connectSuccessful()
|
||||
lastRejection = lastAttempt;
|
||||
}
|
||||
|
||||
synchronized void onFailure() {
|
||||
failures++
|
||||
successes = 0
|
||||
@@ -40,7 +54,17 @@ class Host {
|
||||
failures = 0
|
||||
}
|
||||
|
||||
synchronized void canTryAgain() {
|
||||
System.currentTimeMillis() - lastAttempt > (clearInterval * 60 * 1000)
|
||||
synchronized boolean canTryAgain() {
|
||||
lastSuccessfulAttempt > 0 &&
|
||||
System.currentTimeMillis() - lastAttempt > (clearInterval * 60 * 1000)
|
||||
}
|
||||
|
||||
synchronized boolean isHopeless() {
|
||||
isFailed() &&
|
||||
System.currentTimeMillis() - lastSuccessfulAttempt > (hopelessInterval * 60 * 1000)
|
||||
}
|
||||
|
||||
synchronized boolean isRecentlyRejected() {
|
||||
System.currentTimeMillis() - lastRejection < (rejectionInterval * 60 * 1000)
|
||||
}
|
||||
}
|
||||
|
@@ -52,7 +52,7 @@ class HostCache extends Service {
|
||||
hosts.get(e.destination).clearFailures()
|
||||
return
|
||||
}
|
||||
Host host = new Host(e.destination, settings.hostClearInterval)
|
||||
Host host = new Host(e.destination, settings.hostClearInterval, settings.hostHopelessInterval, settings.hostRejectInterval)
|
||||
if (allowHost(host)) {
|
||||
hosts.put(e.destination, host)
|
||||
}
|
||||
@@ -64,15 +64,17 @@ class HostCache extends Service {
|
||||
Destination dest = e.endpoint.destination
|
||||
Host host = hosts.get(dest)
|
||||
if (host == null) {
|
||||
host = new Host(dest, settings.hostClearInterval)
|
||||
host = new Host(dest, settings.hostClearInterval, settings.hostHopelessInterval, settings.hostRejectInterval)
|
||||
hosts.put(dest, host)
|
||||
}
|
||||
|
||||
switch(e.status) {
|
||||
case ConnectionAttemptStatus.SUCCESSFUL:
|
||||
case ConnectionAttemptStatus.REJECTED:
|
||||
host.onConnect()
|
||||
break
|
||||
case ConnectionAttemptStatus.REJECTED:
|
||||
host.onReject()
|
||||
break
|
||||
case ConnectionAttemptStatus.FAILED:
|
||||
host.onFailure()
|
||||
break
|
||||
@@ -82,6 +84,10 @@ class HostCache extends Service {
|
||||
List<Destination> getHosts(int n) {
|
||||
List<Destination> rv = new ArrayList<>(hosts.keySet())
|
||||
rv.retainAll {allowHost(hosts[it])}
|
||||
rv.removeAll {
|
||||
def h = hosts[it];
|
||||
(h.isFailed() && !h.canTryAgain()) || h.isRecentlyRejected()
|
||||
}
|
||||
if (rv.size() <= n)
|
||||
return rv
|
||||
Collections.shuffle(rv)
|
||||
@@ -106,12 +112,16 @@ class HostCache extends Service {
|
||||
storage.eachLine {
|
||||
def entry = slurper.parseText(it)
|
||||
Destination dest = new Destination(entry.destination)
|
||||
Host host = new Host(dest, settings.hostClearInterval)
|
||||
Host host = new Host(dest, settings.hostClearInterval, settings.hostHopelessInterval, settings.hostRejectInterval)
|
||||
host.failures = Integer.valueOf(String.valueOf(entry.failures))
|
||||
host.successes = Integer.valueOf(String.valueOf(entry.successes))
|
||||
if (entry.lastAttempt != null)
|
||||
host.lastAttempt = entry.lastAttempt
|
||||
if (allowHost(host))
|
||||
if (entry.lastSuccessfulAttempt != null)
|
||||
host.lastSuccessfulAttempt = entry.lastSuccessfulAttempt
|
||||
if (entry.lastRejection != null)
|
||||
host.lastRejection = entry.lastRejection
|
||||
if (allowHost(host))
|
||||
hosts.put(dest, host)
|
||||
}
|
||||
}
|
||||
@@ -120,8 +130,6 @@ class HostCache extends Service {
|
||||
}
|
||||
|
||||
private boolean allowHost(Host host) {
|
||||
if (host.isFailed() && !host.canTryAgain())
|
||||
return false
|
||||
if (host.destination == myself)
|
||||
return false
|
||||
TrustLevel trust = trustService.getLevel(host.destination)
|
||||
@@ -140,12 +148,14 @@ class HostCache extends Service {
|
||||
storage.delete()
|
||||
storage.withPrintWriter { writer ->
|
||||
hosts.each { dest, host ->
|
||||
if (allowHost(host)) {
|
||||
if (allowHost(host) && !host.isHopeless()) {
|
||||
def map = [:]
|
||||
map.destination = dest.toBase64()
|
||||
map.failures = host.failures
|
||||
map.successes = host.successes
|
||||
map.lastAttempt = host.lastAttempt
|
||||
map.lastSuccessfulAttempt = host.lastSuccessfulAttempt
|
||||
map.lastRejection = host.lastRejection
|
||||
def json = JsonOutput.toJson(map)
|
||||
writer.println json
|
||||
}
|
||||
|
@@ -33,11 +33,12 @@ class MeshManager {
|
||||
meshes.get(infoHash)
|
||||
}
|
||||
|
||||
Mesh getOrCreate(InfoHash infoHash, int nPieces) {
|
||||
Mesh getOrCreate(InfoHash infoHash, int nPieces, boolean sequential) {
|
||||
synchronized(meshes) {
|
||||
if (meshes.containsKey(infoHash))
|
||||
return meshes.get(infoHash)
|
||||
Pieces pieces = new Pieces(nPieces, settings.downloadSequentialRatio)
|
||||
float ratio = sequential ? 0f : settings.downloadSequentialRatio
|
||||
Pieces pieces = new Pieces(nPieces, ratio)
|
||||
if (fileManager.rootToFiles.containsKey(infoHash)) {
|
||||
for (int i = 0; i < nPieces; i++)
|
||||
pieces.markDownloaded(i)
|
||||
|
@@ -3,5 +3,5 @@ package com.muwire.core.update
|
||||
import net.i2p.data.Destination
|
||||
|
||||
class UpdateServers {
|
||||
static final Destination UPDATE_SERVER = new Destination("pSWieSRB3czCl3Zz4WpKp4Z8tjv-05zbogRDS7SEnKcSdWOupVwjzQ92GsgQh1VqgoSRk1F8dpZOnHxxz5HFy9D7ri0uFdkMyXdSKoB7IgkkvCfTAyEmeaPwSYnurF3Zk7u286E7YG2rZkQZgJ77tow7ZS0mxFB7Z0Ti-VkZ9~GeGePW~howwNm4iSQACZA0DyTpI8iv5j4I0itPCQRgaGziob~Vfvjk49nd8N4jtaDGo9cEcafikVzQ2OgBgYWL6LRbrrItwuGqsDvITUHWaElUYIDhRQYUq8gYiUA6rwAJputfhFU0J7lIxFR9vVY7YzRvcFckfr0DNI4VQVVlPnRPkUxQa--BlldMaCIppWugjgKLwqiSiHywKpSMlBWgY2z1ry4ueEBo1WEP-mEf88wRk4cFQBCKtctCQnIG2GsnATqTl-VGUAsuzeNWZiFSwXiTy~gQ094yWx-K06fFZUDt4CMiLZVhGlixiInD~34FCRC9LVMtFcqiFB2M-Ql2AAAA")
|
||||
static final Destination UPDATE_SERVER = new Destination("VJYAiCPZHNLraWvLkeRLxRiT4PHAqNqRO1nH240r7u1noBw8Pa~-lJOhKR7CccPkEN8ejSi4H6XjqKYLC8BKLVLeOgnAbedUVx81MV7DETPDdPEGV4RVu6YDFri7-tJOeqauGHxtlXT44YWuR69xKrTG3u4~iTWgxKnlBDht9Q3aVpSPFD2KqEizfVxolqXI0zmAZ2xMi8jfl0oe4GbgHrD9hR2FYj6yKfdqcUgHVobY4kDdJt-u31QqwWdsQMEj8Y3tR2XcNaITEVPiAjoKgBrYwB4jddWPNaT4XdHz76d9p9Iqes7dhOKq3OKpk6kg-bfIKiEOiA1mY49fn5h8pNShTqV7QBhh4CE4EDT3Szl~WsLdrlHUKJufSi7erEMh3coF7HORpF1wah2Xw7q470t~b8dKGKi7N7xQsqhGruDm66PH9oE9Kt9WBVBq2zORdPRtRM61I7EnrwDlbOkL0y~XpvQ3JKUQKdBQ3QsOJt8CHlhHHXMMbvqhntR61RSDBQAEAAcAAA==")
|
||||
}
|
||||
|
@@ -92,7 +92,7 @@ public class UploadManager {
|
||||
pieceSize = downloader.pieceSizePow2
|
||||
} else {
|
||||
SharedFile sharedFile = sharedFiles.iterator().next();
|
||||
mesh = meshManager.getOrCreate(request.infoHash, sharedFile.NPieces)
|
||||
mesh = meshManager.getOrCreate(request.infoHash, sharedFile.NPieces, false)
|
||||
file = sharedFile.file
|
||||
pieceSize = sharedFile.pieceSize
|
||||
}
|
||||
@@ -217,7 +217,7 @@ public class UploadManager {
|
||||
pieceSize = downloader.pieceSizePow2
|
||||
} else {
|
||||
SharedFile sharedFile = sharedFiles.iterator().next();
|
||||
mesh = meshManager.getOrCreate(request.infoHash, sharedFile.NPieces)
|
||||
mesh = meshManager.getOrCreate(request.infoHash, sharedFile.NPieces, false)
|
||||
file = sharedFile.file
|
||||
pieceSize = sharedFile.pieceSize
|
||||
}
|
||||
|
@@ -1,8 +1,20 @@
|
||||
group = com.muwire
|
||||
version = 0.4.9
|
||||
version = 0.4.14
|
||||
groovyVersion = 2.4.15
|
||||
slf4jVersion = 1.7.25
|
||||
spockVersion = 1.1-groovy-2.4
|
||||
grailsVersion=4.0.0
|
||||
gorm.version=7.0.2.RELEASE
|
||||
|
||||
sourceCompatibility=1.8
|
||||
targetCompatibility=1.8
|
||||
|
||||
# plugin properties
|
||||
author = zab@mail.i2p
|
||||
signer = zab@mail.i2p
|
||||
i2pVersion=0.9.41
|
||||
keystorePassword=changeit
|
||||
websiteURL=http://muwire.i2p
|
||||
updateURLsu3=http://muwire.i2p/MuWire.su3
|
||||
|
||||
pack200=true
|
||||
|
@@ -17,6 +17,7 @@ import com.muwire.core.Constants
|
||||
import com.muwire.core.Core
|
||||
import com.muwire.core.Persona
|
||||
import com.muwire.core.SharedFile
|
||||
import com.muwire.core.download.Downloader
|
||||
import com.muwire.core.download.DownloadStartedEvent
|
||||
import com.muwire.core.download.UIDownloadCancelledEvent
|
||||
import com.muwire.core.download.UIDownloadEvent
|
||||
@@ -59,6 +60,7 @@ class MainFrameController {
|
||||
Map<String, Object> params = new HashMap<>()
|
||||
params["search-terms"] = search
|
||||
params["uuid"] = uuid.toString()
|
||||
params["core"] = core
|
||||
def group = mvcGroup.createMVCGroup("SearchTab", uuid.toString(), params)
|
||||
model.results[uuid.toString()] = group
|
||||
|
||||
@@ -84,7 +86,8 @@ class MainFrameController {
|
||||
terms.each { if (it.length() > 0) nonEmpty << it }
|
||||
searchEvent = new SearchEvent(searchTerms : nonEmpty, uuid : uuid, oobInfohash: true)
|
||||
}
|
||||
core.eventBus.publish(new QueryEvent(searchEvent : searchEvent, firstHop : true,
|
||||
boolean firstHop = core.muOptions.allowUntrusted || core.muOptions.searchExtraHop
|
||||
core.eventBus.publish(new QueryEvent(searchEvent : searchEvent, firstHop : firstHop,
|
||||
replyTo: core.me.destination, receivedOn: core.me.destination,
|
||||
originator : core.me))
|
||||
}
|
||||
@@ -96,6 +99,7 @@ class MainFrameController {
|
||||
Map<String, Object> params = new HashMap<>()
|
||||
params["search-terms"] = tabTitle
|
||||
params["uuid"] = uuid.toString()
|
||||
params["core"] = core
|
||||
def group = mvcGroup.createMVCGroup("SearchTab", uuid.toString(), params)
|
||||
model.results[uuid.toString()] = group
|
||||
|
||||
@@ -106,20 +110,6 @@ class MainFrameController {
|
||||
originator : core.me))
|
||||
}
|
||||
|
||||
private def selectedResult() {
|
||||
def selected = builder.getVariable("result-tabs").getSelectedComponent()
|
||||
def group = selected.getClientProperty("mvc-group")
|
||||
def table = selected.getClientProperty("results-table")
|
||||
int row = table.getSelectedRow()
|
||||
if (row == -1)
|
||||
return
|
||||
def sortEvt = group.view.lastSortEvent
|
||||
if (sortEvt != null) {
|
||||
row = group.view.resultsTable.rowSorter.convertRowIndexToModel(row)
|
||||
}
|
||||
group.model.results[row]
|
||||
}
|
||||
|
||||
private int selectedDownload() {
|
||||
def downloadsTable = builder.getVariable("downloads-table")
|
||||
def selected = downloadsTable.getSelectedRow()
|
||||
@@ -129,42 +119,6 @@ class MainFrameController {
|
||||
selected
|
||||
}
|
||||
|
||||
@ControllerAction
|
||||
void download() {
|
||||
def result = selectedResult()
|
||||
if (result == null)
|
||||
return
|
||||
|
||||
if (!model.canDownload(result.infohash))
|
||||
return
|
||||
|
||||
def file = new File(application.context.get("muwire-settings").downloadLocation, result.name)
|
||||
|
||||
def selected = builder.getVariable("result-tabs").getSelectedComponent()
|
||||
def group = selected.getClientProperty("mvc-group")
|
||||
|
||||
def resultsBucket = group.model.hashBucket[result.infohash]
|
||||
def sources = group.model.sourcesBucket[result.infohash]
|
||||
|
||||
core.eventBus.publish(new UIDownloadEvent(result : resultsBucket, sources: sources, target : file))
|
||||
}
|
||||
|
||||
@ControllerAction
|
||||
void trust() {
|
||||
def result = selectedResult()
|
||||
if (result == null)
|
||||
return // TODO disable button
|
||||
core.eventBus.publish( new TrustEvent(persona : result.sender, level : TrustLevel.TRUSTED))
|
||||
}
|
||||
|
||||
@ControllerAction
|
||||
void distrust() {
|
||||
def result = selectedResult()
|
||||
if (result == null)
|
||||
return // TODO disable button
|
||||
core.eventBus.publish( new TrustEvent(persona : result.sender, level : TrustLevel.DISTRUSTED))
|
||||
}
|
||||
|
||||
@ControllerAction
|
||||
void trustPersonaFromSearch() {
|
||||
int selected = builder.getVariable("searches-table").getSelectedRow()
|
||||
@@ -205,6 +159,23 @@ class MainFrameController {
|
||||
core.eventBus.publish(new UIDownloadPausedEvent())
|
||||
}
|
||||
|
||||
@ControllerAction
|
||||
void clear() {
|
||||
def toRemove = []
|
||||
model.downloads.each {
|
||||
if (it.downloader.getCurrentState() == Downloader.DownloadState.CANCELLED) {
|
||||
toRemove << it
|
||||
} else if (it.downloader.getCurrentState() == Downloader.DownloadState.FINISHED) {
|
||||
toRemove << it
|
||||
}
|
||||
}
|
||||
toRemove.each {
|
||||
model.downloads.remove(it)
|
||||
}
|
||||
model.clearButtonEnabled = false
|
||||
|
||||
}
|
||||
|
||||
private void markTrust(String tableName, TrustLevel level, def list) {
|
||||
int row = view.getSelectedTrustTablesRow(tableName)
|
||||
if (row < 0)
|
||||
|
@@ -96,6 +96,10 @@ class OptionsController {
|
||||
model.onlyTrusted = onlyTrusted
|
||||
settings.setAllowUntrusted(!onlyTrusted)
|
||||
|
||||
boolean searchExtraHop = view.searchExtraHopCheckbox.model.isSelected()
|
||||
model.searchExtraHop = searchExtraHop
|
||||
settings.searchExtraHop = searchExtraHop
|
||||
|
||||
boolean trustLists = view.allowTrustListsCheckbox.model.isSelected()
|
||||
model.trustLists = trustLists
|
||||
settings.allowTrustLists = trustLists
|
||||
|
@@ -6,6 +6,75 @@ import griffon.inject.MVCMember
|
||||
import griffon.metadata.ArtifactProviderFor
|
||||
import javax.annotation.Nonnull
|
||||
|
||||
import com.muwire.core.Core
|
||||
import com.muwire.core.download.UIDownloadEvent
|
||||
import com.muwire.core.trust.TrustEvent
|
||||
import com.muwire.core.trust.TrustLevel
|
||||
|
||||
@ArtifactProviderFor(GriffonController)
|
||||
class SearchTabController {
|
||||
|
||||
@MVCMember @Nonnull
|
||||
SearchTabModel model
|
||||
@MVCMember @Nonnull
|
||||
SearchTabView view
|
||||
|
||||
Core core
|
||||
|
||||
private def selectedResult() {
|
||||
int row = view.resultsTable.getSelectedRow()
|
||||
if (row == -1)
|
||||
return null
|
||||
def sortEvt = view.lastSortEvent
|
||||
if (sortEvt != null) {
|
||||
row = view.resultsTable.rowSorter.convertRowIndexToModel(row)
|
||||
}
|
||||
model.results[row]
|
||||
}
|
||||
|
||||
@ControllerAction
|
||||
void download() {
|
||||
def result = selectedResult()
|
||||
if (result == null)
|
||||
return
|
||||
|
||||
if (!mvcGroup.parentGroup.model.canDownload(result.infohash))
|
||||
return
|
||||
|
||||
def file = new File(application.context.get("muwire-settings").downloadLocation, result.name)
|
||||
|
||||
def resultsBucket = model.hashBucket[result.infohash]
|
||||
def sources = model.sourcesBucket[result.infohash]
|
||||
|
||||
core.eventBus.publish(new UIDownloadEvent(result : resultsBucket, sources: sources,
|
||||
target : file, sequential : view.sequentialDownloadCheckbox.model.isSelected()))
|
||||
mvcGroup.parentGroup.view.showDownloadsWindow.call()
|
||||
}
|
||||
|
||||
@ControllerAction
|
||||
void trust() {
|
||||
int row = view.selectedSenderRow()
|
||||
if (row < 0)
|
||||
return
|
||||
def sender = model.senders[row]
|
||||
core.eventBus.publish( new TrustEvent(persona : sender, level : TrustLevel.TRUSTED))
|
||||
}
|
||||
|
||||
@ControllerAction
|
||||
void distrust() {
|
||||
int row = view.selectedSenderRow()
|
||||
if (row < 0)
|
||||
return
|
||||
def sender = model.senders[row]
|
||||
core.eventBus.publish( new TrustEvent(persona : sender, level : TrustLevel.DISTRUSTED))
|
||||
}
|
||||
|
||||
@ControllerAction
|
||||
void neutral() {
|
||||
int row = view.selectedSenderRow()
|
||||
if (row < 0)
|
||||
return
|
||||
def sender = model.senders[row]
|
||||
core.eventBus.publish( new TrustEvent(persona : sender, level : TrustLevel.NEUTRAL))
|
||||
}
|
||||
}
|
@@ -43,7 +43,7 @@ class Initialize extends AbstractLifecycleHandler {
|
||||
|
||||
application.context.put("muwire-home", home.getAbsolutePath())
|
||||
|
||||
System.getProperties().setProperty("awt.useSystemAAFontSettings", "true")
|
||||
System.getProperties().setProperty("awt.useSystemAAFontSettings", "gasp")
|
||||
|
||||
def guiPropsFile = new File(home, "gui.properties")
|
||||
UISettings uiSettings
|
||||
@@ -86,6 +86,8 @@ class Initialize extends AbstractLifecycleHandler {
|
||||
}
|
||||
} else {
|
||||
LookAndFeel chosen = lookAndFeel('system', 'gtk')
|
||||
if (chosen == null)
|
||||
chosen = lookAndFeel('metal')
|
||||
uiSettings.lnf = chosen.getID()
|
||||
log.info("ended up applying $chosen.name")
|
||||
}
|
||||
|
@@ -76,11 +76,10 @@ class MainFrameModel {
|
||||
@Observable String me
|
||||
@Observable int loadedFiles
|
||||
@Observable File hashingFile
|
||||
@Observable boolean downloadActionEnabled
|
||||
@Observable boolean trustButtonsEnabled
|
||||
@Observable boolean cancelButtonEnabled
|
||||
@Observable boolean retryButtonEnabled
|
||||
@Observable boolean pauseButtonEnabled
|
||||
@Observable boolean clearButtonEnabled
|
||||
@Observable String resumeButtonText
|
||||
@Observable boolean subscribeButtonEnabled
|
||||
@Observable boolean markNeutralFromTrustedButtonEnabled
|
||||
@@ -90,8 +89,14 @@ class MainFrameModel {
|
||||
@Observable boolean reviewButtonEnabled
|
||||
@Observable boolean updateButtonEnabled
|
||||
@Observable boolean unsubscribeButtonEnabled
|
||||
|
||||
private final Set<InfoHash> infoHashes = new HashSet<>()
|
||||
|
||||
@Observable boolean searchesPaneButtonEnabled
|
||||
@Observable boolean downloadsPaneButtonEnabled
|
||||
@Observable boolean uploadsPaneButtonEnabled
|
||||
@Observable boolean monitorPaneButtonEnabled
|
||||
@Observable boolean trustPaneButtonEnabled
|
||||
|
||||
@Observable Downloader downloader
|
||||
|
||||
private final Set<InfoHash> downloadInfoHashes = new HashSet<>()
|
||||
|
||||
@@ -119,17 +124,26 @@ class MainFrameModel {
|
||||
return
|
||||
|
||||
// remove cancelled or finished downloads
|
||||
def toRemove = []
|
||||
downloads.each {
|
||||
if (uiSettings.clearCancelledDownloads &&
|
||||
it.downloader.getCurrentState() == Downloader.DownloadState.CANCELLED)
|
||||
toRemove << it
|
||||
if (uiSettings.clearFinishedDownloads &&
|
||||
it.downloader.getCurrentState() == Downloader.DownloadState.FINISHED)
|
||||
toRemove << it
|
||||
}
|
||||
toRemove.each {
|
||||
downloads.remove(it)
|
||||
if (!clearButtonEnabled || uiSettings.clearCancelledDownloads || uiSettings.clearFinishedDownloads) {
|
||||
def toRemove = []
|
||||
downloads.each {
|
||||
if (it.downloader.getCurrentState() == Downloader.DownloadState.CANCELLED) {
|
||||
if (uiSettings.clearCancelledDownloads) {
|
||||
toRemove << it
|
||||
} else {
|
||||
clearButtonEnabled = true
|
||||
}
|
||||
} else if (it.downloader.getCurrentState() == Downloader.DownloadState.FINISHED) {
|
||||
if (uiSettings.clearFinishedDownloads) {
|
||||
toRemove << it
|
||||
} else {
|
||||
clearButtonEnabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
toRemove.each {
|
||||
downloads.remove(it)
|
||||
}
|
||||
}
|
||||
|
||||
builder.getVariable("uploads-table")?.model.fireTableDataChanged()
|
||||
@@ -177,7 +191,7 @@ class MainFrameModel {
|
||||
return
|
||||
int retryInterval = core.muOptions.downloadRetryInterval
|
||||
if (retryInterval > 0) {
|
||||
retryInterval *= 60000
|
||||
retryInterval *= 1000
|
||||
long now = System.currentTimeMillis()
|
||||
if (now - lastRetryTime > retryInterval) {
|
||||
lastRetryTime = now
|
||||
@@ -193,13 +207,19 @@ class MainFrameModel {
|
||||
|
||||
}
|
||||
}
|
||||
}, 60000, 60000)
|
||||
}, 1000, 1000)
|
||||
|
||||
runInsideUIAsync {
|
||||
trusted.addAll(core.trustService.good.values())
|
||||
distrusted.addAll(core.trustService.bad.values())
|
||||
|
||||
resumeButtonText = "Retry"
|
||||
|
||||
searchesPaneButtonEnabled = false
|
||||
downloadsPaneButtonEnabled = true
|
||||
uploadsPaneButtonEnabled = true
|
||||
monitorPaneButtonEnabled = true
|
||||
trustPaneButtonEnabled = true
|
||||
}
|
||||
})
|
||||
|
||||
@@ -288,9 +308,6 @@ class MainFrameModel {
|
||||
}
|
||||
if (e.error != null)
|
||||
return // TODO do something
|
||||
if (infoHashes.contains(e.sharedFile.infoHash))
|
||||
return
|
||||
infoHashes.add(e.sharedFile.infoHash)
|
||||
runInsideUIAsync {
|
||||
shared << e.sharedFile
|
||||
loadedFiles = shared.size()
|
||||
@@ -300,9 +317,6 @@ class MainFrameModel {
|
||||
}
|
||||
|
||||
void onFileLoadedEvent(FileLoadedEvent e) {
|
||||
if (infoHashes.contains(e.loadedFile.infoHash))
|
||||
return
|
||||
infoHashes.add(e.loadedFile.infoHash)
|
||||
runInsideUIAsync {
|
||||
shared << e.loadedFile
|
||||
loadedFiles = shared.size()
|
||||
@@ -312,9 +326,6 @@ class MainFrameModel {
|
||||
}
|
||||
|
||||
void onFileUnsharedEvent(FileUnsharedEvent e) {
|
||||
InfoHash infohash = e.unsharedFile.infoHash
|
||||
if (!infoHashes.remove(infohash))
|
||||
return
|
||||
runInsideUIAsync {
|
||||
shared.remove(e.unsharedFile)
|
||||
loadedFiles = shared.size()
|
||||
@@ -458,7 +469,6 @@ class MainFrameModel {
|
||||
void onFileDownloadedEvent(FileDownloadedEvent e) {
|
||||
if (!core.muOptions.shareDownloadedFiles)
|
||||
return
|
||||
infoHashes.add(e.downloadedFile.infoHash)
|
||||
runInsideUIAsync {
|
||||
shared << e.downloadedFile
|
||||
JTable table = builder.getVariable("shared-files-table")
|
||||
|
@@ -38,6 +38,7 @@ class OptionsModel {
|
||||
|
||||
// trust options
|
||||
@Observable boolean onlyTrusted
|
||||
@Observable boolean searchExtraHop
|
||||
@Observable boolean trustLists
|
||||
@Observable String trustListInterval
|
||||
|
||||
@@ -73,6 +74,7 @@ class OptionsModel {
|
||||
}
|
||||
|
||||
onlyTrusted = !settings.allowUntrusted()
|
||||
searchExtraHop = settings.searchExtraHop
|
||||
trustLists = settings.allowTrustLists
|
||||
trustListInterval = String.valueOf(settings.trustListInterval)
|
||||
}
|
||||
|
@@ -5,6 +5,7 @@ import javax.inject.Inject
|
||||
import javax.swing.JTable
|
||||
|
||||
import com.muwire.core.Core
|
||||
import com.muwire.core.Persona
|
||||
import com.muwire.core.search.UIResultEvent
|
||||
|
||||
import griffon.core.artifact.GriffonModel
|
||||
@@ -17,14 +18,19 @@ import griffon.metadata.ArtifactProviderFor
|
||||
class SearchTabModel {
|
||||
@MVCMember @Nonnull
|
||||
FactoryBuilderSupport builder
|
||||
|
||||
@Observable boolean downloadActionEnabled
|
||||
@Observable boolean trustButtonsEnabled
|
||||
|
||||
Core core
|
||||
UISettings uiSettings
|
||||
String uuid
|
||||
def senders = []
|
||||
def results = []
|
||||
def hashBucket = [:]
|
||||
def sourcesBucket = [:]
|
||||
|
||||
def sendersBucket = new LinkedHashMap<>()
|
||||
|
||||
|
||||
void mvcGroupInit(Map<String, String> args) {
|
||||
core = mvcGroup.parentGroup.model.core
|
||||
@@ -48,6 +54,15 @@ class SearchTabModel {
|
||||
}
|
||||
bucket << e
|
||||
|
||||
def senderBucket = sendersBucket.get(e.sender)
|
||||
if (senderBucket == null) {
|
||||
senderBucket = []
|
||||
sendersBucket[e.sender] = senderBucket
|
||||
senders.clear()
|
||||
senders.addAll(sendersBucket.keySet())
|
||||
}
|
||||
senderBucket << e
|
||||
|
||||
Set sourceBucket = sourcesBucket.get(e.infohash)
|
||||
if (sourceBucket == null) {
|
||||
sourceBucket = new HashSet()
|
||||
@@ -55,8 +70,7 @@ class SearchTabModel {
|
||||
}
|
||||
sourceBucket.addAll(e.sources)
|
||||
|
||||
results << e
|
||||
JTable table = builder.getVariable("results-table")
|
||||
JTable table = builder.getVariable("senders-table")
|
||||
table.model.fireTableDataChanged()
|
||||
}
|
||||
}
|
||||
@@ -72,6 +86,14 @@ class SearchTabModel {
|
||||
bucket = []
|
||||
hashBucket[it.infohash] = bucket
|
||||
}
|
||||
|
||||
def senderBucket = sendersBucket.get(it.sender)
|
||||
if (senderBucket == null) {
|
||||
senderBucket = []
|
||||
sendersBucket[it.sender] = senderBucket
|
||||
senders.clear()
|
||||
senders.addAll(sendersBucket.keySet())
|
||||
}
|
||||
|
||||
Set sourceBucket = sourcesBucket.get(it.infohash)
|
||||
if (sourceBucket == null) {
|
||||
@@ -81,9 +103,9 @@ class SearchTabModel {
|
||||
sourceBucket.addAll(it.sources)
|
||||
|
||||
bucket << it
|
||||
results << it
|
||||
senderBucket << it
|
||||
}
|
||||
JTable table = builder.getVariable("results-table")
|
||||
JTable table = builder.getVariable("senders-table")
|
||||
table.model.fireTableDataChanged()
|
||||
}
|
||||
}
|
||||
|
@@ -89,11 +89,12 @@ class MainFrameView {
|
||||
borderLayout()
|
||||
panel (constraints: BorderLayout.WEST) {
|
||||
gridLayout(rows:1, cols: 2)
|
||||
button(text: "Searches", actionPerformed : showSearchWindow)
|
||||
button(text: "Uploads", actionPerformed : showUploadsWindow)
|
||||
button(text: "Searches", enabled : bind{model.searchesPaneButtonEnabled},actionPerformed : showSearchWindow)
|
||||
button(text: "Downloads", enabled : bind{model.downloadsPaneButtonEnabled}, actionPerformed : showDownloadsWindow)
|
||||
button(text: "Uploads", enabled : bind{model.uploadsPaneButtonEnabled}, actionPerformed : showUploadsWindow)
|
||||
if (settings.showMonitor)
|
||||
button(text: "Monitor", actionPerformed : showMonitorWindow)
|
||||
button(text: "Trust", actionPerformed : showTrustWindow)
|
||||
button(text: "Monitor", enabled: bind{model.monitorPaneButtonEnabled},actionPerformed : showMonitorWindow)
|
||||
button(text: "Trust", enabled:bind{model.trustPaneButtonEnabled},actionPerformed : showTrustWindow)
|
||||
}
|
||||
panel(id: "top-panel", constraints: BorderLayout.CENTER) {
|
||||
cardLayout()
|
||||
@@ -117,18 +118,12 @@ class MainFrameView {
|
||||
cardLayout()
|
||||
panel (constraints : "search window") {
|
||||
borderLayout()
|
||||
splitPane( orientation : JSplitPane.VERTICAL_SPLIT, dividerLocation : 500,
|
||||
continuousLayout : true, constraints : BorderLayout.CENTER) {
|
||||
panel (constraints : JSplitPane.TOP) {
|
||||
borderLayout()
|
||||
tabbedPane(id : "result-tabs", constraints: BorderLayout.CENTER)
|
||||
panel(constraints : BorderLayout.SOUTH) {
|
||||
button(text : "Download", enabled : bind {model.downloadActionEnabled}, downloadAction)
|
||||
button(text : "Trust", enabled: bind {model.trustButtonsEnabled }, trustAction)
|
||||
button(text : "Distrust", enabled : bind {model.trustButtonsEnabled}, distrustAction)
|
||||
}
|
||||
}
|
||||
panel (constraints : JSplitPane.BOTTOM) {
|
||||
tabbedPane(id : "result-tabs", constraints: BorderLayout.CENTER)
|
||||
}
|
||||
panel (constraints: "downloads window") {
|
||||
gridLayout(rows : 1, cols : 1)
|
||||
splitPane(orientation: JSplitPane.VERTICAL_SPLIT, continuousLayout : true, dividerLocation: 500 ) {
|
||||
panel {
|
||||
borderLayout()
|
||||
scrollPane (constraints : BorderLayout.CENTER) {
|
||||
downloadsTable = table(id : "downloads-table", autoCreateRowSorter : true) {
|
||||
@@ -136,7 +131,6 @@ class MainFrameView {
|
||||
closureColumn(header: "Name", preferredWidth: 300, type: String, read : {row -> row.downloader.file.getName()})
|
||||
closureColumn(header: "Status", preferredWidth: 50, type: String, read : {row -> row.downloader.getCurrentState().toString()})
|
||||
closureColumn(header: "Progress", preferredWidth: 70, type: Downloader, read: { row -> row.downloader })
|
||||
closureColumn(header: "Sources", preferredWidth : 10, type: Integer, read : {row -> row.downloader.activeWorkers()})
|
||||
closureColumn(header: "Speed", preferredWidth: 50, type:String, read :{row ->
|
||||
DataHelper.formatSize2Decimal(row.downloader.speed(), false) + "B/sec"
|
||||
})
|
||||
@@ -145,8 +139,39 @@ class MainFrameView {
|
||||
}
|
||||
panel (constraints : BorderLayout.SOUTH) {
|
||||
button(text: "Pause", enabled : bind {model.pauseButtonEnabled}, pauseAction)
|
||||
button(text: "Cancel", enabled : bind {model.cancelButtonEnabled }, cancelAction )
|
||||
button(text: bind { model.resumeButtonText }, enabled : bind {model.retryButtonEnabled}, resumeAction)
|
||||
button(text: "Cancel", enabled : bind {model.cancelButtonEnabled }, cancelAction)
|
||||
button(text: "Clear Done", enabled : bind {model.clearButtonEnabled}, clearAction)
|
||||
}
|
||||
}
|
||||
panel {
|
||||
borderLayout()
|
||||
panel(constraints : BorderLayout.NORTH) {
|
||||
label(text : "Download Details")
|
||||
}
|
||||
scrollPane(constraints : BorderLayout.CENTER) {
|
||||
panel (id : "download-details-panel") {
|
||||
cardLayout()
|
||||
panel (constraints : "select-download") {
|
||||
label(text : "Select a download to view details")
|
||||
}
|
||||
panel(constraints : "download-selected") {
|
||||
gridBagLayout()
|
||||
label(text : "Download Location:", constraints : gbc(gridx:0, gridy:0))
|
||||
label(text : bind {model.downloader?.file?.getAbsolutePath()},
|
||||
constraints: gbc(gridx:1, gridy:0, gridwidth: 2, insets : [0,0,0,20]))
|
||||
label(text : "Piece Size", constraints : gbc(gridx: 0, gridy:1))
|
||||
label(text : bind {model.downloader?.pieceSize}, constraints : gbc(gridx:1, gridy:1))
|
||||
label(text : "Known Sources:", constraints : gbc(gridx:3, gridy: 0))
|
||||
label(text : bind {model.downloader?.activeWorkers?.size()}, constraints : gbc(gridx:4, gridy:0, insets : [0,0,0,20]))
|
||||
label(text : "Active Sources:", constraints : gbc(gridx:3, gridy:1))
|
||||
label(text : bind {model.downloader?.activeWorkers()}, constraints : gbc(gridx:4, gridy:1, insets : [0,0,0,20]))
|
||||
label(text : "Total Pieces:", constraints : gbc(gridx:5, gridy: 0))
|
||||
label(text : bind {model.downloader?.nPieces}, constraints : gbc(gridx:6, gridy:0, insets : [0,0,0,20]))
|
||||
label(text : "Done Pieces:", constraints: gbc(gridx:5, gridy: 1))
|
||||
label(text : bind {model.downloader?.donePieces()}, constraints : gbc(gridx:6, gridy:1, insets : [0,0,0,20]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -371,16 +396,21 @@ class MainFrameView {
|
||||
def selectionModel = downloadsTable.getSelectionModel()
|
||||
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
||||
selectionModel.addListSelectionListener({
|
||||
def downloadDetailsPanel = builder.getVariable("download-details-panel")
|
||||
int selectedRow = selectedDownloaderRow()
|
||||
if (selectedRow < 0) {
|
||||
model.cancelButtonEnabled = false
|
||||
model.retryButtonEnabled = false
|
||||
model.pauseButtonEnabled = false
|
||||
model.downloader = null
|
||||
downloadDetailsPanel.getLayout().show(downloadDetailsPanel,"select-download")
|
||||
return
|
||||
}
|
||||
def downloader = model.downloads[selectedRow]?.downloader
|
||||
if (downloader == null)
|
||||
if (downloader == null)
|
||||
return
|
||||
model.downloader = downloader
|
||||
downloadDetailsPanel.getLayout().show(downloadDetailsPanel,"download-selected")
|
||||
switch(downloader.getCurrentState()) {
|
||||
case Downloader.DownloadState.CONNECTING :
|
||||
case Downloader.DownloadState.DOWNLOADING :
|
||||
@@ -690,21 +720,51 @@ class MainFrameView {
|
||||
def showSearchWindow = {
|
||||
def cardsPanel = builder.getVariable("cards-panel")
|
||||
cardsPanel.getLayout().show(cardsPanel, "search window")
|
||||
model.searchesPaneButtonEnabled = false
|
||||
model.downloadsPaneButtonEnabled = true
|
||||
model.uploadsPaneButtonEnabled = true
|
||||
model.monitorPaneButtonEnabled = true
|
||||
model.trustPaneButtonEnabled = true
|
||||
}
|
||||
|
||||
def showDownloadsWindow = {
|
||||
def cardsPanel = builder.getVariable("cards-panel")
|
||||
cardsPanel.getLayout().show(cardsPanel, "downloads window")
|
||||
model.searchesPaneButtonEnabled = true
|
||||
model.downloadsPaneButtonEnabled = false
|
||||
model.uploadsPaneButtonEnabled = true
|
||||
model.monitorPaneButtonEnabled = true
|
||||
model.trustPaneButtonEnabled = true
|
||||
}
|
||||
|
||||
def showUploadsWindow = {
|
||||
def cardsPanel = builder.getVariable("cards-panel")
|
||||
cardsPanel.getLayout().show(cardsPanel, "uploads window")
|
||||
model.searchesPaneButtonEnabled = true
|
||||
model.downloadsPaneButtonEnabled = true
|
||||
model.uploadsPaneButtonEnabled = false
|
||||
model.monitorPaneButtonEnabled = true
|
||||
model.trustPaneButtonEnabled = true
|
||||
}
|
||||
|
||||
def showMonitorWindow = {
|
||||
def cardsPanel = builder.getVariable("cards-panel")
|
||||
cardsPanel.getLayout().show(cardsPanel,"monitor window")
|
||||
model.searchesPaneButtonEnabled = true
|
||||
model.downloadsPaneButtonEnabled = true
|
||||
model.uploadsPaneButtonEnabled = true
|
||||
model.monitorPaneButtonEnabled = false
|
||||
model.trustPaneButtonEnabled = true
|
||||
}
|
||||
|
||||
def showTrustWindow = {
|
||||
def cardsPanel = builder.getVariable("cards-panel")
|
||||
cardsPanel.getLayout().show(cardsPanel,"trust window")
|
||||
model.searchesPaneButtonEnabled = true
|
||||
model.downloadsPaneButtonEnabled = true
|
||||
model.uploadsPaneButtonEnabled = true
|
||||
model.monitorPaneButtonEnabled = true
|
||||
model.trustPaneButtonEnabled = false
|
||||
}
|
||||
|
||||
def shareFiles = {
|
||||
|
@@ -55,6 +55,7 @@ class OptionsView {
|
||||
def outBwField
|
||||
|
||||
def allowUntrustedCheckbox
|
||||
def searchExtraHopCheckbox
|
||||
def allowTrustListsCheckbox
|
||||
def trustListIntervalField
|
||||
|
||||
@@ -70,7 +71,7 @@ class OptionsView {
|
||||
gridBagLayout()
|
||||
label(text : "Retry failed downloads every", constraints : gbc(gridx: 0, gridy: 0))
|
||||
retryField = textField(text : bind { model.downloadRetryInterval }, columns : 2, constraints : gbc(gridx: 1, gridy: 0))
|
||||
label(text : "minutes", constraints : gbc(gridx : 2, gridy: 0))
|
||||
label(text : "seconds", constraints : gbc(gridx : 2, gridy: 0))
|
||||
|
||||
label(text : "Check for updates every", constraints : gbc(gridx : 0, gridy: 1))
|
||||
updateField = textField(text : bind {model.updateCheckInterval }, columns : 2, constraints : gbc(gridx : 1, gridy: 1))
|
||||
@@ -117,9 +118,9 @@ class OptionsView {
|
||||
fontField = textField(text : bind {model.font}, columns : 4, constraints : gbc(gridx : 1, gridy:2))
|
||||
// label(text : "Show Monitor", constraints : gbc(gridx :0, gridy: 3))
|
||||
// monitorCheckbox = checkBox(selected : bind {model.showMonitor}, constraints : gbc(gridx : 1, gridy: 3))
|
||||
label(text : "Clear Cancelled Downloads", constraints: gbc(gridx: 0, gridy:4))
|
||||
label(text : "Automatically Clear Cancelled Downloads", constraints: gbc(gridx: 0, gridy:4))
|
||||
clearCancelledDownloadsCheckbox = checkBox(selected : bind {model.clearCancelledDownloads}, constraints : gbc(gridx : 1, gridy:4))
|
||||
label(text : "Clear Finished Downloads", constraints: gbc(gridx: 0, gridy:5))
|
||||
label(text : "Automatically Clear Finished Downloads", constraints: gbc(gridx: 0, gridy:5))
|
||||
clearFinishedDownloadsCheckbox = checkBox(selected : bind {model.clearFinishedDownloads}, constraints : gbc(gridx : 1, gridy:5))
|
||||
label(text : "Exclude Local Files From Results", constraints: gbc(gridx:0, gridy:6))
|
||||
excludeLocalResultCheckbox = checkBox(selected : bind {model.excludeLocalResult}, constraints : gbc(gridx: 1, gridy : 6))
|
||||
@@ -138,11 +139,13 @@ class OptionsView {
|
||||
gridBagLayout()
|
||||
label(text : "Allow only trusted connections", constraints : gbc(gridx: 0, gridy : 0))
|
||||
allowUntrustedCheckbox = checkBox(selected : bind {model.onlyTrusted}, constraints : gbc(gridx: 1, gridy : 0))
|
||||
label(text : "Allow others to view my trust list", constraints : gbc(gridx: 0, gridy : 1))
|
||||
allowTrustListsCheckbox = checkBox(selected : bind {model.trustLists}, constraints : gbc(gridx: 1, gridy : 1))
|
||||
label(text : "Update trust lists every ", constraints : gbc(gridx:0, gridy:2))
|
||||
trustListIntervalField = textField(text : bind {model.trustListInterval}, constraints:gbc(gridx:1, gridy:2))
|
||||
label(text : "hours", constraints : gbc(gridx: 2, gridy:2))
|
||||
label(text : "Search extra hop", constraints : gbc(gridx:0, gridy:1))
|
||||
searchExtraHopCheckbox = checkBox(selected : bind {model.searchExtraHop}, constraints : gbc(gridx: 1, gridy : 1))
|
||||
label(text : "Allow others to view my trust list", constraints : gbc(gridx: 0, gridy : 2))
|
||||
allowTrustListsCheckbox = checkBox(selected : bind {model.trustLists}, constraints : gbc(gridx: 1, gridy : 2))
|
||||
label(text : "Update trust lists every ", constraints : gbc(gridx:0, gridy:3))
|
||||
trustListIntervalField = textField(text : bind {model.trustListInterval}, constraints:gbc(gridx:1, gridy:3))
|
||||
label(text : "hours", constraints : gbc(gridx: 2, gridy:3))
|
||||
}
|
||||
|
||||
|
||||
|
183
gui/griffon-app/views/com/muwire/gui/OptionsView.groovy.orig
Normal file
@@ -0,0 +1,183 @@
|
||||
package com.muwire.gui
|
||||
|
||||
import griffon.core.artifact.GriffonView
|
||||
import griffon.inject.MVCMember
|
||||
import griffon.metadata.ArtifactProviderFor
|
||||
|
||||
import javax.swing.JDialog
|
||||
import javax.swing.JPanel
|
||||
import javax.swing.JTabbedPane
|
||||
import javax.swing.SwingConstants
|
||||
|
||||
import com.muwire.core.Core
|
||||
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.event.WindowAdapter
|
||||
import java.awt.event.WindowEvent
|
||||
|
||||
import javax.annotation.Nonnull
|
||||
|
||||
@ArtifactProviderFor(GriffonView)
|
||||
class OptionsView {
|
||||
@MVCMember @Nonnull
|
||||
FactoryBuilderSupport builder
|
||||
@MVCMember @Nonnull
|
||||
OptionsModel model
|
||||
|
||||
def d
|
||||
def p
|
||||
def i
|
||||
def u
|
||||
def bandwidth
|
||||
def trust
|
||||
|
||||
def retryField
|
||||
def updateField
|
||||
def autoDownloadUpdateCheckbox
|
||||
def shareDownloadedCheckbox
|
||||
|
||||
def inboundLengthField
|
||||
def inboundQuantityField
|
||||
def outboundLengthField
|
||||
def outboundQuantityField
|
||||
def i2pUDPPortField
|
||||
def i2pNTCPPortField
|
||||
|
||||
def lnfField
|
||||
def monitorCheckbox
|
||||
def fontField
|
||||
def clearCancelledDownloadsCheckbox
|
||||
def clearFinishedDownloadsCheckbox
|
||||
def excludeLocalResultCheckbox
|
||||
def showSearchHashesCheckbox
|
||||
|
||||
def inBwField
|
||||
def outBwField
|
||||
|
||||
def allowUntrustedCheckbox
|
||||
def allowTrustListsCheckbox
|
||||
def trustListIntervalField
|
||||
|
||||
def buttonsPanel
|
||||
|
||||
def mainFrame
|
||||
|
||||
void initUI() {
|
||||
mainFrame = application.windowManager.findWindow("main-frame")
|
||||
d = new JDialog(mainFrame, "Options", true)
|
||||
d.setResizable(false)
|
||||
p = builder.panel {
|
||||
gridBagLayout()
|
||||
label(text : "Retry failed downloads every", constraints : gbc(gridx: 0, gridy: 0))
|
||||
retryField = textField(text : bind { model.downloadRetryInterval }, columns : 2, constraints : gbc(gridx: 1, gridy: 0))
|
||||
label(text : "minutes", constraints : gbc(gridx : 2, gridy: 0))
|
||||
|
||||
label(text : "Check for updates every", constraints : gbc(gridx : 0, gridy: 1))
|
||||
updateField = textField(text : bind {model.updateCheckInterval }, columns : 2, constraints : gbc(gridx : 1, gridy: 1))
|
||||
label(text : "hours", constraints : gbc(gridx: 2, gridy : 1))
|
||||
|
||||
label(text : "Download updates automatically", constraints: gbc(gridx :0, gridy : 2))
|
||||
autoDownloadUpdateCheckbox = checkBox(selected : bind {model.autoDownloadUpdate}, constraints : gbc(gridx:1, gridy : 2))
|
||||
|
||||
label(text : "Share downloaded files", constraints : gbc(gridx : 0, gridy:3))
|
||||
shareDownloadedCheckbox = checkBox(selected : bind {model.shareDownloadedFiles}, constraints : gbc(gridx :1, gridy:3))
|
||||
|
||||
label(text : "Save downloaded files to:", constraints: gbc(gridx:0, gridy:4))
|
||||
button(text : "Choose", constraints : gbc(gridx : 1, gridy:4), downloadLocationAction)
|
||||
label(text : bind {model.downloadLocation}, constraints: gbc(gridx:0, gridy:5, gridwidth:2))
|
||||
|
||||
}
|
||||
i = builder.panel {
|
||||
gridBagLayout()
|
||||
label(text : "Changing these settings requires a restart", constraints : gbc(gridx : 0, gridy : 0, gridwidth: 2))
|
||||
label(text : "Inbound Length", constraints : gbc(gridx:0, gridy:1))
|
||||
inboundLengthField = textField(text : bind {model.inboundLength}, columns : 2, constraints : gbc(gridx:1, gridy:1))
|
||||
label(text : "Inbound Quantity", constraints : gbc(gridx:0, gridy:2))
|
||||
inboundQuantityField = textField(text : bind {model.inboundQuantity}, columns : 2, constraints : gbc(gridx:1, gridy:2))
|
||||
label(text : "Outbound Length", constraints : gbc(gridx:0, gridy:3))
|
||||
outboundLengthField = textField(text : bind {model.outboundLength}, columns : 2, constraints : gbc(gridx:1, gridy:3))
|
||||
label(text : "Outbound Quantity", constraints : gbc(gridx:0, gridy:4))
|
||||
outboundQuantityField = textField(text : bind {model.outboundQuantity}, columns : 2, constraints : gbc(gridx:1, gridy:4))
|
||||
|
||||
Core core = application.context.get("core")
|
||||
if (core.router != null) {
|
||||
label(text : "TCP Port", constraints : gbc(gridx :0, gridy: 5))
|
||||
i2pNTCPPortField = textField(text : bind {model.i2pNTCPPort}, columns : 4, constraints : gbc(gridx:1, gridy:5))
|
||||
label(text : "UDP Port", constraints : gbc(gridx :0, gridy: 6))
|
||||
i2pUDPPortField = textField(text : bind {model.i2pUDPPort}, columns : 4, constraints : gbc(gridx:1, gridy:6))
|
||||
}
|
||||
|
||||
}
|
||||
u = builder.panel {
|
||||
gridBagLayout()
|
||||
label(text : "Changing these settings requires a restart", constraints : gbc(gridx : 0, gridy : 0, gridwidth: 2))
|
||||
label(text : "Look And Feel", constraints : gbc(gridx: 0, gridy:1))
|
||||
lnfField = textField(text : bind {model.lnf}, columns : 4, constraints : gbc(gridx : 1, gridy : 1))
|
||||
label(text : "Font", constraints : gbc(gridx: 0, gridy : 2))
|
||||
fontField = textField(text : bind {model.font}, columns : 4, constraints : gbc(gridx : 1, gridy:2))
|
||||
// label(text : "Show Monitor", constraints : gbc(gridx :0, gridy: 3))
|
||||
// monitorCheckbox = checkBox(selected : bind {model.showMonitor}, constraints : gbc(gridx : 1, gridy: 3))
|
||||
label(text : "Clear Cancelled Downloads", constraints: gbc(gridx: 0, gridy:4))
|
||||
clearCancelledDownloadsCheckbox = checkBox(selected : bind {model.clearCancelledDownloads}, constraints : gbc(gridx : 1, gridy:4))
|
||||
label(text : "Clear Finished Downloads", constraints: gbc(gridx: 0, gridy:5))
|
||||
clearFinishedDownloadsCheckbox = checkBox(selected : bind {model.clearFinishedDownloads}, constraints : gbc(gridx : 1, gridy:5))
|
||||
label(text : "Exclude Local Files From Results", constraints: gbc(gridx:0, gridy:6))
|
||||
excludeLocalResultCheckbox = checkBox(selected : bind {model.excludeLocalResult}, constraints : gbc(gridx: 1, gridy : 6))
|
||||
// label(text : "Show Hash Searches In Monitor", constraints: gbc(gridx:0, gridy:7))
|
||||
// showSearchHashesCheckbox = checkBox(selected : bind {model.showSearchHashes}, constraints : gbc(gridx: 1, gridy: 7))
|
||||
}
|
||||
bandwidth = builder.panel {
|
||||
gridBagLayout()
|
||||
label(text : "Changing these settings requires a restart", constraints : gbc(gridx : 0, gridy : 0, gridwidth: 2))
|
||||
label(text : "Inbound bandwidth (KB)", constraints : gbc(gridx: 0, gridy : 1))
|
||||
inBwField = textField(text : bind {model.inBw}, columns : 3, constraints : gbc(gridx : 1, gridy : 1))
|
||||
label(text : "Outbound bandwidth (KB)", constraints : gbc(gridx: 0, gridy : 2))
|
||||
outBwField = textField(text : bind {model.outBw}, columns : 3, constraints : gbc(gridx : 1, gridy : 2))
|
||||
}
|
||||
trust = builder.panel {
|
||||
gridBagLayout()
|
||||
label(text : "Allow only trusted connections", constraints : gbc(gridx: 0, gridy : 0))
|
||||
allowUntrustedCheckbox = checkBox(selected : bind {model.onlyTrusted}, constraints : gbc(gridx: 1, gridy : 0))
|
||||
label(text : "Allow others to view my trust list", constraints : gbc(gridx: 0, gridy : 1))
|
||||
allowTrustListsCheckbox = checkBox(selected : bind {model.trustLists}, constraints : gbc(gridx: 1, gridy : 1))
|
||||
label(text : "Update trust lists every ", constraints : gbc(gridx:0, gridy:2))
|
||||
trustListIntervalField = textField(text : bind {model.trustListInterval}, constraints:gbc(gridx:1, gridy:2))
|
||||
label(text : "hours", constraints : gbc(gridx: 2, gridy:2))
|
||||
}
|
||||
|
||||
|
||||
buttonsPanel = builder.panel {
|
||||
gridBagLayout()
|
||||
button(text : "Save", constraints : gbc(gridx : 1, gridy: 2), saveAction)
|
||||
button(text : "Cancel", constraints : gbc(gridx : 2, gridy: 2), cancelAction)
|
||||
}
|
||||
}
|
||||
|
||||
void mvcGroupInit(Map<String,String> args) {
|
||||
def tabbedPane = new JTabbedPane()
|
||||
tabbedPane.addTab("MuWire", p)
|
||||
tabbedPane.addTab("I2P", i)
|
||||
tabbedPane.addTab("GUI", u)
|
||||
Core core = application.context.get("core")
|
||||
if (core.router != null) {
|
||||
tabbedPane.addTab("Bandwidth", bandwidth)
|
||||
}
|
||||
tabbedPane.addTab("Trust", trust)
|
||||
|
||||
JPanel panel = new JPanel()
|
||||
panel.setLayout(new BorderLayout())
|
||||
panel.add(tabbedPane, BorderLayout.CENTER)
|
||||
panel.add(buttonsPanel, BorderLayout.SOUTH)
|
||||
|
||||
d.getContentPane().add(panel)
|
||||
d.pack()
|
||||
d.setLocationRelativeTo(mainFrame)
|
||||
d.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE)
|
||||
d.addWindowListener(new WindowAdapter() {
|
||||
public void windowClosed(WindowEvent e) {
|
||||
mvcGroup.destroy()
|
||||
}
|
||||
})
|
||||
d.show()
|
||||
}
|
||||
}
|
@@ -11,15 +11,19 @@ import javax.swing.JComponent
|
||||
import javax.swing.JLabel
|
||||
import javax.swing.JMenuItem
|
||||
import javax.swing.JPopupMenu
|
||||
import javax.swing.JSplitPane
|
||||
import javax.swing.JTable
|
||||
import javax.swing.ListSelectionModel
|
||||
import javax.swing.SwingConstants
|
||||
import javax.swing.table.DefaultTableCellRenderer
|
||||
|
||||
import com.muwire.core.Persona
|
||||
import com.muwire.core.util.DataUtil
|
||||
|
||||
import java.awt.BorderLayout
|
||||
import java.awt.Color
|
||||
import java.awt.FlowLayout
|
||||
import java.awt.GridBagConstraints
|
||||
import java.awt.Toolkit
|
||||
import java.awt.datatransfer.StringSelection
|
||||
import java.awt.event.MouseAdapter
|
||||
@@ -37,23 +41,64 @@ class SearchTabView {
|
||||
def pane
|
||||
def parent
|
||||
def searchTerms
|
||||
def sendersTable
|
||||
def lastSendersSortEvent
|
||||
def resultsTable
|
||||
def lastSortEvent
|
||||
def sequentialDownloadCheckbox
|
||||
|
||||
void initUI() {
|
||||
builder.with {
|
||||
def resultsTable
|
||||
def pane = scrollPane {
|
||||
resultsTable = table(id : "results-table", autoCreateRowSorter : true) {
|
||||
tableModel(list: model.results) {
|
||||
closureColumn(header: "Name", preferredWidth: 350, type: String, read : {row -> row.name.replace('<','_')})
|
||||
closureColumn(header: "Size", preferredWidth: 20, type: Long, read : {row -> row.size})
|
||||
closureColumn(header: "Direct Sources", preferredWidth: 50, type : Integer, read : { row -> model.hashBucket[row.infohash].size()})
|
||||
closureColumn(header: "Possible Sources", preferredWidth : 50, type : Integer, read : {row -> model.sourcesBucket[row.infohash].size()})
|
||||
closureColumn(header: "Sender", preferredWidth: 170, type: String, read : {row -> row.sender.getHumanReadableName()})
|
||||
closureColumn(header: "Trust", preferredWidth: 50, type: String, read : {row ->
|
||||
model.core.trustService.getLevel(row.sender.destination).toString()
|
||||
})
|
||||
def sendersTable
|
||||
def sequentialDownloadCheckbox
|
||||
def pane = panel {
|
||||
gridLayout(rows :1, cols : 1)
|
||||
splitPane(orientation: JSplitPane.VERTICAL_SPLIT, continuousLayout : true, dividerLocation: 300 ) {
|
||||
panel {
|
||||
borderLayout()
|
||||
scrollPane (constraints : BorderLayout.CENTER) {
|
||||
sendersTable = table(id : "senders-table", autoCreateRowSorter : true) {
|
||||
tableModel(list : model.senders) {
|
||||
closureColumn(header : "Sender", preferredWidth : 500, type: String, read : {row -> row.getHumanReadableName()})
|
||||
closureColumn(header : "Results", preferredWidth : 20, type: Integer, read : {row -> model.sendersBucket[row].size()})
|
||||
closureColumn(header : "Trust", preferredWidth : 50, type: String, read : { row ->
|
||||
model.core.trustService.getLevel(row.destination).toString()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
panel(constraints : BorderLayout.SOUTH) {
|
||||
button(text : "Trust", enabled: bind {model.trustButtonsEnabled }, trustAction)
|
||||
button(text : "Neutral", enabled: bind {model.trustButtonsEnabled}, neutralAction)
|
||||
button(text : "Distrust", enabled : bind {model.trustButtonsEnabled}, distrustAction)
|
||||
}
|
||||
}
|
||||
panel {
|
||||
borderLayout()
|
||||
scrollPane (constraints : BorderLayout.CENTER) {
|
||||
resultsTable = table(id : "results-table", autoCreateRowSorter : true) {
|
||||
tableModel(list: model.results) {
|
||||
closureColumn(header: "Name", preferredWidth: 350, type: String, read : {row -> row.name.replace('<','_')})
|
||||
closureColumn(header: "Size", preferredWidth: 20, type: Long, read : {row -> row.size})
|
||||
closureColumn(header: "Direct Sources", preferredWidth: 50, type : Integer, read : { row -> model.hashBucket[row.infohash].size()})
|
||||
closureColumn(header: "Possible Sources", preferredWidth : 50, type : Integer, read : {row -> model.sourcesBucket[row.infohash].size()})
|
||||
}
|
||||
}
|
||||
}
|
||||
panel(constraints : BorderLayout.SOUTH) {
|
||||
gridLayout(rows: 1, cols: 3)
|
||||
panel()
|
||||
panel {
|
||||
button(text : "Download", enabled : bind {model.downloadActionEnabled}, downloadAction)
|
||||
}
|
||||
panel {
|
||||
gridBagLayout()
|
||||
panel (constraints : gbc(gridx : 0, gridy : 0, weightx : 100))
|
||||
sequentialDownloadCheckbox = checkBox(constraints : gbc(gridx : 1, gridy: 0, weightx : 0),selected : false, enabled : bind {model.downloadActionEnabled})
|
||||
label(constraints: gbc(gridx: 2, gridy: 0, weightx : 0),text : "Download sequentially")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,17 +108,20 @@ class SearchTabView {
|
||||
this.pane.putClientProperty("results-table",resultsTable)
|
||||
|
||||
this.resultsTable = resultsTable
|
||||
this.sendersTable = sendersTable
|
||||
this.sequentialDownloadCheckbox = sequentialDownloadCheckbox
|
||||
|
||||
def selectionModel = resultsTable.getSelectionModel()
|
||||
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
||||
selectionModel.addListSelectionListener( {
|
||||
int row = resultsTable.getSelectedRow()
|
||||
if (row < 0)
|
||||
if (row < 0) {
|
||||
model.downloadActionEnabled = false
|
||||
return
|
||||
}
|
||||
if (lastSortEvent != null)
|
||||
row = resultsTable.rowSorter.convertRowIndexToModel(row)
|
||||
mvcGroup.parentGroup.model.trustButtonsEnabled = true
|
||||
mvcGroup.parentGroup.model.downloadActionEnabled = mvcGroup.parentGroup.model.canDownload(model.results[row].infohash)
|
||||
model.downloadActionEnabled = mvcGroup.parentGroup.model.canDownload(model.results[row].infohash)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -98,12 +146,11 @@ class SearchTabView {
|
||||
}
|
||||
|
||||
parent.setTabComponentAt(index, tabPanel)
|
||||
mvcGroup.parentGroup.view.showSearchWindow.call()
|
||||
|
||||
def centerRenderer = new DefaultTableCellRenderer()
|
||||
centerRenderer.setHorizontalAlignment(JLabel.CENTER)
|
||||
resultsTable.columnModel.getColumn(1).setCellRenderer(centerRenderer)
|
||||
resultsTable.setDefaultRenderer(Integer.class,centerRenderer)
|
||||
resultsTable.columnModel.getColumn(4).setCellRenderer(centerRenderer)
|
||||
|
||||
resultsTable.columnModel.getColumn(1).setCellRenderer(new SizeRenderer())
|
||||
|
||||
@@ -118,7 +165,7 @@ class SearchTabView {
|
||||
if (e.button == MouseEvent.BUTTON3)
|
||||
showPopupMenu(e)
|
||||
else if (e.button == MouseEvent.BUTTON1 && e.clickCount == 2)
|
||||
mvcGroup.parentGroup.controller.download()
|
||||
mvcGroup.controller.download()
|
||||
}
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
@@ -126,21 +173,42 @@ class SearchTabView {
|
||||
showPopupMenu(e)
|
||||
}
|
||||
})
|
||||
|
||||
// senders table
|
||||
sendersTable.setDefaultRenderer(Integer.class, centerRenderer)
|
||||
sendersTable.rowSorter.addRowSorterListener({evt -> lastSendersSortEvent = evt})
|
||||
sendersTable.rowSorter.setSortsOnUpdates(true)
|
||||
def selectionModel = sendersTable.getSelectionModel()
|
||||
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)
|
||||
selectionModel.addListSelectionListener({
|
||||
int row = selectedSenderRow()
|
||||
if (row < 0) {
|
||||
model.trustButtonsEnabled = false
|
||||
return
|
||||
} else {
|
||||
model.trustButtonsEnabled = true
|
||||
model.results.clear()
|
||||
Persona p = model.senders[row]
|
||||
model.results.addAll(model.sendersBucket[p])
|
||||
resultsTable.model.fireTableDataChanged()
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
def closeTab = {
|
||||
int index = parent.indexOfTab(searchTerms)
|
||||
parent.removeTabAt(index)
|
||||
mvcGroup.parentGroup.model.trustButtonsEnabled = false
|
||||
mvcGroup.parentGroup.model.downloadActionEnabled = false
|
||||
model.trustButtonsEnabled = false
|
||||
model.downloadActionEnabled = false
|
||||
mvcGroup.destroy()
|
||||
}
|
||||
|
||||
def showPopupMenu(MouseEvent e) {
|
||||
JPopupMenu menu = new JPopupMenu()
|
||||
if (mvcGroup.parentGroup.model.downloadActionEnabled) {
|
||||
if (model.downloadActionEnabled) {
|
||||
JMenuItem download = new JMenuItem("Download")
|
||||
download.addActionListener({mvcGroup.parentGroup.controller.download()})
|
||||
download.addActionListener({mvcGroup.controller.download()})
|
||||
menu.add(download)
|
||||
}
|
||||
JMenuItem copyHashToClipboard = new JMenuItem("Copy hash to clipboard")
|
||||
@@ -160,4 +228,13 @@ class SearchTabView {
|
||||
def clipboard = Toolkit.getDefaultToolkit().getSystemClipboard()
|
||||
clipboard.setContents(selection, null)
|
||||
}
|
||||
|
||||
int selectedSenderRow() {
|
||||
int row = sendersTable.getSelectedRow()
|
||||
if (row < 0)
|
||||
return -1
|
||||
if (lastSendersSortEvent != null)
|
||||
row = sendersTable.rowSorter.convertRowIndexToModel(row)
|
||||
row
|
||||
}
|
||||
}
|
@@ -24,7 +24,7 @@ class DownloadProgressRenderer extends DefaultTableCellRenderer {
|
||||
if (pieces != 0)
|
||||
percent = (done * 100 / pieces)
|
||||
String totalSize = DataHelper.formatSize2Decimal(d.length, false) + "B"
|
||||
setText(String.format("%2d", percent) + "% of ${totalSize} ($done/$pieces pcs)".toString())
|
||||
setText(String.format("%2d", percent) + "% of ${totalSize}".toString())
|
||||
|
||||
if (isSelected) {
|
||||
setForeground(table.getSelectionForeground())
|
||||
|
123
plug/build.gradle
Normal file
@@ -0,0 +1,123 @@
|
||||
buildscript {
|
||||
repositories { mavenCentral() }
|
||||
dependencies {
|
||||
classpath fileTree("../i2pjars") { include '*.jar' }
|
||||
classpath 'gnu.getopt:java-getopt:1.0.13'
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin : 'java'
|
||||
|
||||
dependencies {
|
||||
compile project(":webui")
|
||||
}
|
||||
|
||||
def buildDir = new File("$buildDir")
|
||||
def zipDir = new File(buildDir, "zip")
|
||||
def libDir = new File(zipDir, "lib")
|
||||
def consoleDir = new File(zipDir, "console")
|
||||
def webAppDir = new File(consoleDir, "webapps")
|
||||
def keystore = new File(System.getProperty("user.home")+"/.i2p-plugin-keys/plugin-su3-keystore.ks")
|
||||
|
||||
|
||||
String libDirPath() {
|
||||
def libDir = new File("$buildDir/zip/lib")
|
||||
libDir.listFiles().stream().map({
|
||||
"\$PLUGIN/lib/" + it.getName()
|
||||
}).collect(java.util.stream.Collectors.joining(','))
|
||||
}
|
||||
|
||||
task pluginConfig {
|
||||
doLast {
|
||||
def binding = [ "version" : project.version + "-b" + project.buildNumber,
|
||||
"author" : project.author,
|
||||
"signer" : project.signer,
|
||||
"websiteURL" : project.websiteURL,
|
||||
"updateURLsu3" : project.updateURLsu3,
|
||||
"i2pVersion" : project.i2pVersion ]
|
||||
|
||||
def templateFile = new File("$projectDir/templates/plugin.config.template")
|
||||
def engine = new groovy.text.SimpleTemplateEngine()
|
||||
def output = engine.createTemplate(templateFile).make(binding)
|
||||
|
||||
zipDir.mkdirs()
|
||||
|
||||
def outputFile = new File(zipDir,"plugin.config")
|
||||
outputFile.text = output
|
||||
}
|
||||
}
|
||||
|
||||
task pluginDir {
|
||||
dependsOn ':webui:assemble'
|
||||
doLast { task ->
|
||||
libDir.mkdirs()
|
||||
def webapp = project(":webui")
|
||||
def i2pjars = task.project.fileTree("../i2pjars").getFiles()
|
||||
webapp.configurations.runtime.each {
|
||||
if (!i2pjars.contains(it)) {
|
||||
def dest = new File(libDir, it.getName())
|
||||
java.nio.file.Files.copy(it.toPath(), dest.toPath())
|
||||
}
|
||||
}
|
||||
|
||||
webAppDir.mkdirs()
|
||||
def warFile = webapp.configurations.warArtifact.getAllArtifacts().file[0]
|
||||
def dest = new File(webAppDir, "MuWire.war")
|
||||
java.nio.file.Files.copy(warFile.toPath(), dest.toPath())
|
||||
"zip -d ${dest.toString()} *.jar".execute()
|
||||
}
|
||||
}
|
||||
|
||||
task webappConfig {
|
||||
doLast {
|
||||
def cPath = "webapps.MuWire.classpath=" + libDirPath()
|
||||
def webappConfig = new File(consoleDir, "webapps.config")
|
||||
webappConfig.text = cPath
|
||||
}
|
||||
}
|
||||
|
||||
task pack {
|
||||
doLast {
|
||||
if (project.pack200 == "true") {
|
||||
libDir.listFiles().stream().filter( { it.getName().endsWith(".jar") } ).
|
||||
filter({it.length() > 512*1024}).forEach {
|
||||
println "packing $it"
|
||||
def name = it.toString()
|
||||
println "pack200 --no-gzip ${name}.pack $name".execute().text
|
||||
it.delete()
|
||||
}
|
||||
} else {
|
||||
println "pack200 disabled"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
task pluginZip(type: Zip) {
|
||||
archiveFileName = "MuWire.zip"
|
||||
destinationDirectory = buildDir
|
||||
from zipDir
|
||||
}
|
||||
|
||||
task sign {
|
||||
doLast {
|
||||
def password = project.keystorePassword
|
||||
if (password == "") {
|
||||
println "enter your keystore password:"
|
||||
def reader = new BufferedReader(new InputStreamReader(System.in))
|
||||
password = reader.readLine()
|
||||
}
|
||||
def su3args = []
|
||||
def version = project.version + "-b" + project.buildNumber
|
||||
su3args << 'sign' << '-t' << '6' << '-c' << 'PLUGIN' << '-f' << '0' << '-p' << password << \
|
||||
"$buildDir/MuWire.zip".toString() << "$buildDir/MuWire.su3".toString() << \
|
||||
keystore.getAbsoluteFile().toString() << version << project.signer
|
||||
println "now enter private key password for signer $project.signer"
|
||||
net.i2p.crypto.SU3File.main(su3args.toArray(new String[0]))
|
||||
}
|
||||
}
|
||||
|
||||
webappConfig.dependsOn pluginDir
|
||||
pack.dependsOn webappConfig
|
||||
pluginZip.dependsOn(webappConfig,pluginConfig,pack)
|
||||
sign.dependsOn pluginZip
|
||||
assemble.dependsOn sign
|
14
plug/templates/plugin.config.template
Normal file
@@ -0,0 +1,14 @@
|
||||
name=MuWire
|
||||
description=Easy Anonymous File-Sharing
|
||||
license=GPLv3
|
||||
version=${version}
|
||||
author=${author}
|
||||
signer=${signer}
|
||||
websiteURL=${websiteURL}
|
||||
updateURL.su3=${updateURLsu3}
|
||||
min-i2p-version=${i2pVersion}
|
||||
min-java-version=1.8
|
||||
consoleLinkName=MuWire
|
||||
consoleLinkTooltip=Anonymous File Sharing
|
||||
consoleLinkURL=/MuWire
|
||||
<% println "date="+System.currentTimeMillis() %>
|
@@ -4,3 +4,5 @@ include 'update-server'
|
||||
include 'core'
|
||||
include 'gui'
|
||||
include 'cli'
|
||||
// include 'webui'
|
||||
// include 'plug'
|
||||
|
@@ -9,6 +9,7 @@ import net.i2p.client.I2PSession
|
||||
import net.i2p.client.I2PSessionMuxedListener
|
||||
import net.i2p.client.datagram.I2PDatagramDissector
|
||||
import net.i2p.client.datagram.I2PDatagramMaker
|
||||
import net.i2p.crypto.SigType
|
||||
|
||||
|
||||
@Log
|
||||
@@ -28,7 +29,7 @@ class UpdateServer {
|
||||
def session
|
||||
if (!keyFile.exists()) {
|
||||
def os = new FileOutputStream(keyFile);
|
||||
myDest = i2pClient.createDestination(os)
|
||||
myDest = i2pClient.createDestination(os, SigType.EdDSA_SHA512_Ed25519)
|
||||
os.close()
|
||||
log.info "No key.dat file was found, so creating a new destination."
|
||||
log.info "This is the destination you want to give out for your new UpdateServer"
|
||||
|
12
webui/.gitignore
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
Thumbs.db
|
||||
.DS_Store
|
||||
.gradle
|
||||
build/
|
||||
out/
|
||||
.idea
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.project
|
||||
.settings
|
||||
.classpath
|
109
webui/build.gradle
Normal file
@@ -0,0 +1,109 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
maven { url "https://repo.grails.org/grails/core" }
|
||||
}
|
||||
dependencies {
|
||||
classpath "org.grails:grails-gradle-plugin:$grailsVersion"
|
||||
classpath "org.grails.plugins:hibernate5:7.0.0"
|
||||
classpath "gradle.plugin.com.github.erdi.webdriver-binaries:webdriver-binaries-gradle-plugin:2.0"
|
||||
classpath "com.bertramlabs.plugins:asset-pipeline-gradle:3.0.10"
|
||||
}
|
||||
}
|
||||
|
||||
version "0.1"
|
||||
group "webui"
|
||||
|
||||
apply plugin:"eclipse"
|
||||
apply plugin:"idea"
|
||||
apply plugin:"war"
|
||||
apply plugin:"org.grails.grails-web"
|
||||
apply plugin:"com.github.erdi.webdriver-binaries"
|
||||
apply plugin:"org.grails.grails-gsp"
|
||||
apply plugin:"com.bertramlabs.asset-pipeline"
|
||||
|
||||
repositories {
|
||||
maven { url "https://repo.grails.org/grails/core" }
|
||||
}
|
||||
|
||||
configurations {
|
||||
developmentOnly
|
||||
runtimeClasspath {
|
||||
extendsFrom developmentOnly
|
||||
}
|
||||
warArtifact
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile project(":core")
|
||||
developmentOnly("org.springframework.boot:spring-boot-devtools")
|
||||
compile "org.springframework.boot:spring-boot-starter-logging"
|
||||
compile "org.springframework.boot:spring-boot-autoconfigure"
|
||||
compile "org.grails:grails-core"
|
||||
compile "org.springframework.boot:spring-boot-starter-actuator"
|
||||
provided "org.springframework.boot:spring-boot-starter-tomcat"
|
||||
compile "org.grails:grails-web-boot"
|
||||
compile "org.grails:grails-logging"
|
||||
compile "org.grails:grails-plugin-rest"
|
||||
compile "org.grails:grails-plugin-databinding"
|
||||
compile "org.grails:grails-plugin-i18n"
|
||||
compile "org.grails:grails-plugin-services"
|
||||
compile "org.grails:grails-plugin-url-mappings"
|
||||
compile "org.grails:grails-plugin-interceptors"
|
||||
compile "org.grails.plugins:cache"
|
||||
compile "org.grails.plugins:async"
|
||||
compile "org.grails.plugins:scaffolding"
|
||||
compile "org.grails.plugins:events"
|
||||
compile "org.grails.plugins:hibernate5"
|
||||
compile "org.hibernate:hibernate-core:5.4.0.Final"
|
||||
compile "org.grails.plugins:gsp"
|
||||
compileOnly "io.micronaut:micronaut-inject-groovy"
|
||||
console "org.grails:grails-console"
|
||||
profile "org.grails.profiles:web"
|
||||
runtime "org.glassfish.web:el-impl:2.1.2-b03"
|
||||
runtime "com.h2database:h2"
|
||||
runtime "org.apache.tomcat:tomcat-jdbc"
|
||||
runtime "javax.xml.bind:jaxb-api:2.3.0"
|
||||
runtime "com.bertramlabs.plugins:asset-pipeline-grails:3.0.10"
|
||||
testCompile "org.grails:grails-gorm-testing-support"
|
||||
testCompile "org.mockito:mockito-core"
|
||||
testCompile "org.grails:grails-web-testing-support"
|
||||
testCompile "org.grails.plugins:geb"
|
||||
testCompile "org.seleniumhq.selenium:selenium-remote-driver:3.14.0"
|
||||
testCompile "org.seleniumhq.selenium:selenium-api:3.14.0"
|
||||
testCompile "org.seleniumhq.selenium:selenium-support:3.14.0"
|
||||
testRuntime "org.seleniumhq.selenium:selenium-chrome-driver:3.14.0"
|
||||
testRuntime "org.seleniumhq.selenium:selenium-firefox-driver:3.14.0"
|
||||
}
|
||||
|
||||
bootRun {
|
||||
jvmArgs(
|
||||
'-Dspring.output.ansi.enabled=always',
|
||||
'-noverify',
|
||||
'-XX:TieredStopAtLevel=1',
|
||||
'-Xmx1024m')
|
||||
sourceResources sourceSets.main
|
||||
String springProfilesActive = 'spring.profiles.active'
|
||||
systemProperty springProfilesActive, System.getProperty(springProfilesActive)
|
||||
}
|
||||
|
||||
webdriverBinaries {
|
||||
chromedriver '2.45.0'
|
||||
geckodriver '0.24.0'
|
||||
}
|
||||
|
||||
tasks.withType(Test) {
|
||||
systemProperty "geb.env", System.getProperty('geb.env')
|
||||
systemProperty "geb.build.reportsDir", reporting.file("geb/integrationTest")
|
||||
systemProperty "webdriver.chrome.driver", System.getProperty('webdriver.chrome.driver')
|
||||
systemProperty "webdriver.gecko.driver", System.getProperty('webdriver.gecko.driver')
|
||||
}
|
||||
|
||||
|
||||
assets {
|
||||
minifyJs = true
|
||||
minifyCss = true
|
||||
}
|
||||
|
||||
artifacts {
|
||||
warArtifact war
|
||||
}
|
27
webui/grails-app/assets/images/advancedgrails.svg
Normal file
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="93.58px" height="93.58px" viewBox="0 0 93.58 93.58" enable-background="new 0 0 93.58 93.58" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<circle fill="none" stroke="#FEB672" stroke-width="2.8347" stroke-miterlimit="10" cx="46.79" cy="46.789" r="45.374"/>
|
||||
</g>
|
||||
<g>
|
||||
<path fill="#FEB672" d="M71.126,29.576c0,0.414-0.337,0.75-0.75,0.75h-3.25v3.25c0,0.415-0.337,0.751-0.751,0.751h-1.499
|
||||
c-0.415,0-0.75-0.336-0.75-0.751v-3.25h-3.251c-0.414,0-0.749-0.336-0.749-0.75v-1.498c0-0.416,0.335-0.752,0.749-0.752h3.251
|
||||
v-3.249c0-0.414,0.335-0.75,0.75-0.75h1.499c0.414,0,0.751,0.336,0.751,0.75v3.249h3.25c0.413,0,0.75,0.336,0.75,0.752V29.576z"/>
|
||||
</g>
|
||||
<path fill="#FEB672" d="M50.42,60.386c0.554,1.467,0.855,1.951,1.493,3.44c0.271,0.627,0.523,1.228,0.649,1.518
|
||||
c0.049,0.117,0.036,0.248-0.033,0.355c-0.172,0.259-0.552,0.747-1.181,1.086c-1.098,0.594-3.409,0.809-4.555,0.812h-0.006
|
||||
c-1.146-0.004-3.457-0.219-4.558-0.812c-0.627-0.339-1.006-0.827-1.177-1.086c-0.07-0.107-0.083-0.238-0.032-0.355
|
||||
c0.123-0.29,0.376-0.891,0.646-1.518c0.64-1.489,0.941-1.974,1.495-3.44c0.485-1.294,0.729-3.175,0.745-4.593
|
||||
c0.006-0.604-0.03-1.122-0.106-1.476c-0.121-0.56-0.501-1.412-0.907-2.042c-0.548-0.849-1.527-1.583-2.157-1.919
|
||||
c-0.475-0.254-1.984-0.817-2.576-1.146c-0.755-0.416-1.739-1.067-2.399-1.584c-0.735-0.574-2.182-1.992-2.746-2.695
|
||||
c-1.084-1.344-2.083-2.922-2.565-4.62c-0.601-2.106-0.576-3.009-0.657-3.688c-0.014-0.117,0.075-0.222,0.191-0.227
|
||||
c0.73-0.025,3.854-0.093,16.809-0.081c12.953-0.012,16.076,0.056,16.806,0.081c0.118,0.005,0.206,0.109,0.191,0.227
|
||||
c-0.08,0.68-0.057,1.582-0.654,3.688c-0.486,1.698-1.483,3.276-2.567,4.62c-0.564,0.703-2.011,2.121-2.746,2.695
|
||||
c-0.661,0.517-1.646,1.168-2.399,1.584c-0.594,0.328-2.102,0.892-2.576,1.146c-0.63,0.336-1.608,1.07-2.158,1.919
|
||||
c-0.405,0.63-0.785,1.482-0.904,2.042c-0.079,0.354-0.112,0.872-0.107,1.476C49.69,57.211,49.935,59.092,50.42,60.386z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.2 KiB |
BIN
webui/grails-app/assets/images/apple-touch-icon-retina.png
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
webui/grails-app/assets/images/apple-touch-icon.png
Normal file
After Width: | Height: | Size: 3.0 KiB |
19
webui/grails-app/assets/images/documentation.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Ebene_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
width="93.58px" height="93.58px" viewBox="0 0 93.58 93.58" enable-background="new 0 0 93.58 93.58" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<circle fill="none" stroke="#FEB672" stroke-width="2.8347" stroke-miterlimit="10" cx="46.88" cy="46.792" r="45.374"/>
|
||||
</g>
|
||||
<path fill="#FEB672" d="M64.379,40.958v24.062c0,1.208-0.979,2.188-2.188,2.188H31.567c-1.208,0-2.188-0.979-2.188-2.188V28.562
|
||||
c0-1.208,0.98-2.188,2.188-2.188h18.229v12.396c0,1.208,0.979,2.188,2.188,2.188H64.379z M55.629,44.604
|
||||
c0-0.41-0.318-0.729-0.729-0.729H38.858c-0.41,0-0.729,0.319-0.729,0.729v1.458c0,0.41,0.319,0.729,0.729,0.729H54.9
|
||||
c0.41,0,0.729-0.319,0.729-0.729V44.604z M55.629,50.438c0-0.41-0.318-0.729-0.729-0.729H38.858c-0.41,0-0.729,0.319-0.729,0.729
|
||||
v1.458c0,0.41,0.319,0.729,0.729,0.729H54.9c0.41,0,0.729-0.319,0.729-0.729V50.438z M55.629,56.271
|
||||
c0-0.41-0.318-0.729-0.729-0.729H38.858c-0.41,0-0.729,0.319-0.729,0.729v1.458c0,0.41,0.319,0.729,0.729,0.729H54.9
|
||||
c0.41,0,0.729-0.319,0.729-0.729V56.271z M63.468,38.042H52.713V27.287c0.318,0.205,0.592,0.41,0.82,0.638l9.297,9.297
|
||||
C63.059,37.449,63.264,37.723,63.468,38.042z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.4 KiB |
BIN
webui/grails-app/assets/images/favicon.ico
Normal file
After Width: | Height: | Size: 5.4 KiB |
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0"?>
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1000" height="500">
|
||||
<desc iVinci="yes" version="4.5" gridStep="20" showGrid="no" snapToGrid="no" codePlatform="0"/>
|
||||
<g id="Layer1" opacity="1">
|
||||
<g id="Shape1">
|
||||
<desc shapeID="1" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-74.3391,-50.75,148.678,101.5)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(4.79624,0,0,4.79624,500,250)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath1" d="M527.264,491.011 C544.051,488.613 563.236,483.817 572.829,479.021 C582.421,474.224 589.615,467.03 589.615,462.234 C589.615,462.234 587.217,457.438 584.819,452.641 C580.023,445.447 575.227,435.854 563.236,409.475 C558.44,397.484 547.589,366.072 544.051,351.92 C540.386,330.773 540.051,308.254 544.051,287.171 C547.531,274.839 552.314,262.919 560.838,253.597 C570.402,240.945 581.622,228.467 596.81,222.422 C644.094,203.599 699.929,162.469 728.707,116.904 C738.299,100.117 742.876,92.923 746.372,83.3305 C755.023,59.5988 762.66,34.3876 762.28,8.98871 L762.28,6.59059 L498.487,6.59059 L232.295,6.59059 L232.295,11.3868 C231.901,74.2274 269.048,130.868 313.831,172.061 C337.813,193.644 366.59,210.431 400.164,222.422 C412.154,227.218 416.951,229.616 426.543,239.208 C438.534,253.597 448.126,270.384 452.923,289.569 C455.827,317.286 453.654,346.577 445.728,373.503 L440.932,387.892 C438.534,397.484 431.339,411.873 419.349,435.854 C407.358,459.836 407.358,462.234 407.358,464.632 C412.154,479.021 440.932,488.613 484.098,493.409 C493.691,493.409 508.079,493.409 527.264,491.011 M325.822,409.475 C342.609,407.077 356.998,402.281 361.794,395.086 L361.794,392.688 L359.396,385.494 C342.609,354.318 333.016,327.939 333.016,301.56 C333.016,287.171 335.415,279.977 340.211,267.986 C347.405,255.995 349.803,252.125 361.794,247.329 C366.59,244.876 372.313,243.95 374.711,242.478 C380.979,240.625 388.173,236.81 388.173,236.81 C388.173,236.81 383.868,235.884 379.016,233.486 C364.628,228.69 359.396,224.82 347.405,217.625 C309.035,196.042 285.054,174.459 261.073,143.284 C253.878,131.293 250.156,125.996 246.684,121.163 L244.286,116.904 C241.888,114.506 145.963,114.506 143.565,116.904 C141.939,150.478 158.03,180.057 179.536,205.635 C204.661,235.514 225.101,244.005 244.286,248.801 C261.073,253.597 263.471,255.995 270.665,265.588 C275.462,277.578 277.86,284.773 277.86,299.161 C280.258,320.745 273.063,342.328 258.675,373.503 C253.878,383.096 249.082,392.688 249.082,392.688 C249.082,395.086 253.878,399.883 258.675,402.281 C270.665,409.475 304.239,414.271 325.822,409.475 M716.716,409.475 C735.901,407.077 747.892,402.281 750.29,395.086 C750.29,392.688 750.29,390.29 743.095,375.901 C728.008,346.118 717.597,310.72 726.308,277.578 C731.287,264.162 737.689,250.182 752.688,247.852 C776.669,240.658 795.854,229.616 819.835,205.635 C834.224,191.246 847.61,166.971 851.369,152.876 C854.382,141.577 858.172,128.066 855.807,116.904 C853.409,114.506 755.086,114.506 752.688,116.904 C752.688,116.904 750.29,119.302 747.892,121.7 C745.493,128.895 735.901,143.284 728.707,150.478 C719.114,162.469 690.337,191.246 680.744,198.44 C663.057,216.559 629.114,228.768 611.199,236.81 C613.597,239.208 625.587,246.403 635.18,248.801 C654.365,255.995 654.365,255.995 661.559,267.986 C666.355,279.977 668.754,287.171 668.754,301.56 C670.08,334.844 653.109,365.67 639.976,392.688 C657.022,411.883 692.824,411.394 716.716,409.475 Z" style="stroke:none;fill-rule:evenodd;fill:#ffffff;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape2">
|
||||
<desc shapeID="2" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-3.75,-28,7.5,56)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(1,0,0,1,417.25,99.5)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath2" d="M413.5,127.5 C414,126.5 416,123 416.5,122.5 C416,123 414,126.5 413.5,127.5 M421,71.5 " style="stroke:none;fill-rule:evenodd;fill:#669020;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape3">
|
||||
<desc shapeID="3" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(0,0,0,0)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(1,0,0,1,0,0)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath3" d="M0,0 Z" style="stroke:none;fill-rule:evenodd;fill:#4c4c4c;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape4">
|
||||
<desc shapeID="4" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(0,0,0,0)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(1,0,0,1,0,0)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath4" d="M0,0 Z" style="stroke:none;fill-rule:evenodd;fill:#000000;fill-opacity:1;"/>
|
||||
</g>
|
||||
<g id="Shape5">
|
||||
<desc shapeID="5" type="0" basicInfo-basicType="0" basicInfo-roundedRectRadius="12" basicInfo-polygonSides="6" basicInfo-starPoints="5" bounding="rect(-84.6928,-47.6497,169.386,95.2993)" text="" font-familyName="" font-pixelSize="20" font-bold="0" font-underline="0" font-alignment="1" strokeStyle="0" markerStart="0" markerEnd="0" shadowEnabled="0" shadowOffsetX="0" shadowOffsetY="2" shadowBlur="4" shadowOpacity="160" blurEnabled="0" blurRadius="4" transform="matrix(1,0,0,1,90.9499,90.9738)" pers-center="0,0" pers-size="0,0" pers-start="0,0" pers-end="0,0" locked="0" mesh="" flag=""/>
|
||||
<path id="shapePath5" d="M0,0 Z" style="stroke:none;fill-rule:evenodd;fill:#0d0d0d;fill-opacity:1;"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 6.8 KiB |
13
webui/grails-app/assets/images/grails.svg
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
webui/grails-app/assets/images/skin/database_add.png
Normal file
After Width: | Height: | Size: 658 B |
BIN
webui/grails-app/assets/images/skin/database_delete.png
Normal file
After Width: | Height: | Size: 659 B |
BIN
webui/grails-app/assets/images/skin/database_edit.png
Normal file
After Width: | Height: | Size: 767 B |
BIN
webui/grails-app/assets/images/skin/database_save.png
Normal file
After Width: | Height: | Size: 755 B |
BIN
webui/grails-app/assets/images/skin/database_table.png
Normal file
After Width: | Height: | Size: 726 B |
BIN
webui/grails-app/assets/images/skin/exclamation.png
Normal file
After Width: | Height: | Size: 701 B |
BIN
webui/grails-app/assets/images/skin/house.png
Normal file
After Width: | Height: | Size: 806 B |
BIN
webui/grails-app/assets/images/skin/information.png
Normal file
After Width: | Height: | Size: 778 B |
BIN
webui/grails-app/assets/images/skin/shadow.jpg
Normal file
After Width: | Height: | Size: 300 B |
BIN
webui/grails-app/assets/images/skin/sorted_asc.gif
Normal file
After Width: | Height: | Size: 835 B |
BIN
webui/grails-app/assets/images/skin/sorted_desc.gif
Normal file
After Width: | Height: | Size: 834 B |
18
webui/grails-app/assets/images/slack.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="45px" height="45px" viewBox="0 0 45 45" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 52.1 (67048) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>slack_orange</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="slack_orange">
|
||||
<path d="M22.502,0 C10.073,0 0,10.073 0,22.499 C0,34.927 10.073,45 22.502,45 C34.927,45 45,34.927 45,22.499 C45,10.073 34.927,0 22.502,0 Z" id="Shape" fill="#FEB672"></path>
|
||||
<g id="Slack_Mark_Monochrome_White" transform="translate(11.000000, 11.000000)" fill="#FFFFFF">
|
||||
<rect id="Rectangle-path" transform="translate(11.554441, 11.462704) rotate(-18.518296) translate(-11.554441, -11.462704) " x="9.94812935" y="9.91115274" width="3.21262291" height="3.10310168"></rect>
|
||||
<g id="Group">
|
||||
<rect id="Rectangle-path" transform="translate(11.554441, 11.462704) rotate(-18.518296) translate(-11.554441, -11.462704) " x="9.94812935" y="9.91115274" width="3.21262291" height="3.10310168"></rect>
|
||||
<path d="M22.0142857,8.30555556 C19.6595238,0.456349206 16.2642857,-1.36904762 8.41507937,0.985714286 C0.565873016,3.34047619 -1.25952381,6.73571429 1.0952381,14.5849206 C3.45,22.434127 6.8452381,24.2595238 14.6944444,21.9047619 C22.5436508,19.55 24.3690476,16.1547619 22.0142857,8.30555556 Z M18.0531746,13.3984127 L16.5746032,13.8912698 L17.0857143,15.4246032 C17.2865079,16.0452381 16.9579365,16.7206349 16.3373016,16.9214286 C16.2095238,16.9579365 16.0634921,16.9944444 15.9357143,16.9761905 C15.4611111,16.9579365 15.0047619,16.647619 14.8404762,16.1730159 L14.3293651,14.6396825 L11.2809524,15.6619048 L11.7920635,17.1952381 C11.9928571,17.815873 11.6642857,18.4912698 11.0436508,18.6920635 C10.915873,18.7285714 10.7698413,18.7650794 10.6420635,18.7468254 C10.1674603,18.7285714 9.71111111,18.418254 9.5468254,17.9436508 L9.03571429,16.4103175 L7.55714286,16.9031746 C7.42936508,16.9396825 7.28333333,16.9761905 7.15555556,16.9579365 C6.68095238,16.9396825 6.22460317,16.6293651 6.06031746,16.1547619 C5.85952381,15.534127 6.18809524,14.8587302 6.80873016,14.6579365 L8.28730159,14.1650794 L7.3015873,11.2261905 L5.82301587,11.7190476 C5.6952381,11.7555556 5.54920635,11.7920635 5.42142857,11.7738095 C4.9468254,11.7555556 4.49047619,11.4452381 4.32619048,10.9706349 C4.12539683,10.35 4.45396825,9.67460317 5.07460317,9.47380952 L6.5531746,8.98095238 L6.04206349,7.44761905 C5.84126984,6.82698413 6.16984127,6.1515873 6.79047619,5.95079365 C7.41111111,5.75 8.08650794,6.07857143 8.28730159,6.69920635 L8.7984127,8.23253968 L11.8468254,7.21031746 L11.3357143,5.67698413 C11.1349206,5.05634921 11.4634921,4.38095238 12.084127,4.18015873 C12.7047619,3.97936508 13.3801587,4.30793651 13.5809524,4.92857143 L14.0920635,6.46190476 L15.5706349,5.96904762 C16.1912698,5.76825397 16.8666667,6.0968254 17.0674603,6.71746032 C17.268254,7.33809524 16.9396825,8.01349206 16.3190476,8.21428571 L14.8404762,8.70714286 L15.8261905,11.6460317 L17.3047619,11.1531746 C17.9253968,10.952381 18.6007937,11.2809524 18.8015873,11.9015873 C19.002381,12.5222222 18.6738095,13.197619 18.0531746,13.3984127 Z" id="Shape" fill-rule="nonzero"></path>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.3 KiB |
BIN
webui/grails-app/assets/images/spinner.gif
Normal file
After Width: | Height: | Size: 2.0 KiB |
11
webui/grails-app/assets/javascripts/application.js
Normal file
@@ -0,0 +1,11 @@
|
||||
// This is a manifest file that'll be compiled into application.js.
|
||||
//
|
||||
// Any JavaScript file within this directory can be referenced here using a relative path.
|
||||
//
|
||||
// You're free to add application-wide JavaScript to this file, but it's generally better
|
||||
// to create separate JavaScript files as needed.
|
||||
//
|
||||
//= require jquery-3.3.1.min
|
||||
//= require bootstrap
|
||||
//= require popper.min
|
||||
//= require_self
|
6461
webui/grails-app/assets/javascripts/bootstrap.bundle.js
vendored
Normal file
7
webui/grails-app/assets/javascripts/bootstrap.bundle.min.js
vendored
Normal file
3944
webui/grails-app/assets/javascripts/bootstrap.js
vendored
Normal file
1
webui/grails-app/assets/javascripts/bootstrap.js.map
Normal file
7
webui/grails-app/assets/javascripts/bootstrap.min.js
vendored
Normal file
1
webui/grails-app/assets/javascripts/bootstrap.min.js.map
Normal file
2
webui/grails-app/assets/javascripts/jquery-3.3.1.min.js
vendored
Normal file
5
webui/grails-app/assets/javascripts/popper.min.js
vendored
Normal file
15
webui/grails-app/assets/stylesheets/application.css
Normal file
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
||||
* listed below.
|
||||
*
|
||||
* Any CSS file within this directory can be referenced here using a relative path.
|
||||
*
|
||||
* You're free to add application-wide styles to this file and they'll appear at the top of the
|
||||
* compiled file, but it's generally better to create a new file per style scope.
|
||||
*
|
||||
*= require bootstrap
|
||||
*= require grails
|
||||
*= require main
|
||||
*= require mobile
|
||||
*= require_self
|
||||
*/
|
1912
webui/grails-app/assets/stylesheets/bootstrap-grid.css
vendored
Normal file
331
webui/grails-app/assets/stylesheets/bootstrap-reboot.css
vendored
Normal file
@@ -0,0 +1,331 @@
|
||||
/*!
|
||||
* Bootstrap Reboot v4.1.3 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2018 The Bootstrap Authors
|
||||
* Copyright 2011-2018 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||
*/
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: sans-serif;
|
||||
line-height: 1.15;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-ms-overflow-style: scrollbar;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
@-ms-viewport {
|
||||
width: device-width;
|
||||
}
|
||||
|
||||
article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #212529;
|
||||
text-align: left;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
[tabindex="-1"]:focus {
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
hr {
|
||||
box-sizing: content-box;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
abbr[title],
|
||||
abbr[data-original-title] {
|
||||
text-decoration: underline;
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
cursor: help;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 1rem;
|
||||
font-style: normal;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol ol,
|
||||
ul ul,
|
||||
ol ul,
|
||||
ul ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: .5rem;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
position: relative;
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
background-color: transparent;
|
||||
-webkit-text-decoration-skip: objects;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #0056b3;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]) {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]):focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
pre,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
-ms-overflow-style: scrollbar;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
img {
|
||||
vertical-align: middle;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
svg {
|
||||
overflow: hidden;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: 0.75rem;
|
||||
padding-bottom: 0.75rem;
|
||||
color: #6c757d;
|
||||
text-align: left;
|
||||
caption-side: bottom;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: inherit;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: 1px dotted;
|
||||
outline: 5px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
optgroup,
|
||||
textarea {
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
input {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
button,
|
||||
html [type="button"],
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
input[type="radio"],
|
||||
input[type="checkbox"] {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
input[type="date"],
|
||||
input[type="time"],
|
||||
input[type="datetime-local"],
|
||||
input[type="month"] {
|
||||
-webkit-appearance: listbox;
|
||||
}
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 0;
|
||||
margin-bottom: .5rem;
|
||||
font-size: 1.5rem;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[type="search"] {
|
||||
outline-offset: -2px;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
[type="search"]::-webkit-search-cancel-button,
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap-reboot.css.map */
|
9030
webui/grails-app/assets/stylesheets/bootstrap.css
vendored
Normal file
1
webui/grails-app/assets/stylesheets/bootstrap.css.map
Normal file
7
webui/grails-app/assets/stylesheets/bootstrap.min.css
vendored
Normal file
109
webui/grails-app/assets/stylesheets/errors.css
Normal file
@@ -0,0 +1,109 @@
|
||||
h1, h2 {
|
||||
margin: 10px 25px 5px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.filename {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.exceptionMessage {
|
||||
margin: 10px;
|
||||
border: 1px solid #000;
|
||||
padding: 5px;
|
||||
background-color: #E9E9E9;
|
||||
}
|
||||
|
||||
.stack,
|
||||
.snippet {
|
||||
margin: 0 25px 10px;
|
||||
}
|
||||
|
||||
.stack,
|
||||
.snippet {
|
||||
border: 1px solid #ccc;
|
||||
-mox-box-shadow: 0 0 2px rgba(0,0,0,0.2);
|
||||
-webkit-box-shadow: 0 0 2px rgba(0,0,0,0.2);
|
||||
box-shadow: 0 0 2px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
/* error details */
|
||||
.error-details {
|
||||
border-top: 1px solid #FFAAAA;
|
||||
-mox-box-shadow: 0 0 2px rgba(0,0,0,0.2);
|
||||
-webkit-box-shadow: 0 0 2px rgba(0,0,0,0.2);
|
||||
box-shadow: 0 0 2px rgba(0,0,0,0.2);
|
||||
border-bottom: 1px solid #FFAAAA;
|
||||
-mox-box-shadow: 0 0 2px rgba(0,0,0,0.2);
|
||||
-webkit-box-shadow: 0 0 2px rgba(0,0,0,0.2);
|
||||
box-shadow: 0 0 2px rgba(0,0,0,0.2);
|
||||
background-color:#FFF3F3;
|
||||
line-height: 1.5;
|
||||
overflow: hidden;
|
||||
padding: 5px;
|
||||
padding-left:25px;
|
||||
}
|
||||
|
||||
.error-details dt {
|
||||
clear: left;
|
||||
float: left;
|
||||
font-weight: bold;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.error-details dt:after {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
.error-details dd {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* stack trace */
|
||||
.stack {
|
||||
padding: 5px;
|
||||
overflow: auto;
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
/* code snippet */
|
||||
.snippet {
|
||||
background-color: #fff;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.snippet .line {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.snippet .lineNumber {
|
||||
background-color: #ddd;
|
||||
color: #999;
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
padding: 0 3px;
|
||||
text-align: right;
|
||||
width: 3em;
|
||||
}
|
||||
|
||||
.snippet .error {
|
||||
background-color: #fff3f3;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.snippet .error .lineNumber {
|
||||
background-color: #faa;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.snippet .line:first-child .lineNumber {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
.snippet .line:last-child .lineNumber {
|
||||
padding-bottom: 5px;
|
||||
}
|
1078
webui/grails-app/assets/stylesheets/grails.css
Normal file
594
webui/grails-app/assets/stylesheets/main.css
Normal file
@@ -0,0 +1,594 @@
|
||||
/* FONT STACK */
|
||||
body,
|
||||
input, select, textarea {
|
||||
font-family: "Open Sans", "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
/* BASE LAYOUT */
|
||||
|
||||
html {
|
||||
background-color: #ddd;
|
||||
background-image: -moz-linear-gradient(center top, #aaa, #ddd);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #aaa), color-stop(1, #ddd));
|
||||
background-image: linear-gradient(to bottom, #aaa, #ddd);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorStr = '#aaaaaa', EndColorStr = '#dddddd');
|
||||
background-repeat: no-repeat;
|
||||
height: 100%;
|
||||
/* change the box model to exclude the padding from the calculation of 100% height (IE8+) */
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html.no-cssgradients {
|
||||
background-color: #aaa;
|
||||
}
|
||||
|
||||
html * {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #F5F5F5;
|
||||
color: #333333;
|
||||
overflow-x: hidden; /* prevents box-shadow causing a horizontal scrollbar in firefox when viewport < 960px wide */
|
||||
-moz-box-shadow: 0 0 0.3em #424649;
|
||||
-webkit-box-shadow: 0 0 0.3em #424649;
|
||||
box-shadow: 0 0 0.3em #424649;
|
||||
}
|
||||
|
||||
#grailsLogo {
|
||||
background-color: #feb672;
|
||||
}
|
||||
|
||||
a:hover, a:active {
|
||||
outline: none; /* prevents outline in webkit on active links but retains it for tab focus */
|
||||
}
|
||||
|
||||
h1, h2, h3 {
|
||||
font-weight: normal;
|
||||
font-size: 1.25em;
|
||||
margin: 0.8em 0 0.3em 0;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/* GENERAL */
|
||||
|
||||
#grailsLogo a {
|
||||
display: inline-block;
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
.content {
|
||||
}
|
||||
|
||||
.content h1 {
|
||||
border-bottom: 1px solid #CCCCCC;
|
||||
margin: 0.8em 1em 0.3em;
|
||||
padding: 0 0.25em;
|
||||
}
|
||||
|
||||
.scaffold-list h1 {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.footer img {
|
||||
height: 80px;
|
||||
margin-right: 25px;
|
||||
margin-bottom: 15px;
|
||||
clear: bottom;
|
||||
}
|
||||
|
||||
.footer strong a {
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.footer {
|
||||
background: #424649;
|
||||
color: #ffffff;
|
||||
clear: both;
|
||||
font-size: 1em;
|
||||
margin-top: 1.5em;
|
||||
padding: 1em;
|
||||
padding-bottom: 2em;
|
||||
min-height: 1em;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
color: #feb672;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
background: url(../images/spinner.gif) 50% 50% no-repeat transparent;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
padding: 0.5em;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
text-indent: -9999px;
|
||||
}
|
||||
|
||||
/* NAVIGATION MENU */
|
||||
|
||||
.nav {
|
||||
zoom: 1;
|
||||
}
|
||||
|
||||
.nav ul {
|
||||
overflow: hidden;
|
||||
padding-left: 0;
|
||||
zoom: 1;
|
||||
}
|
||||
|
||||
.nav li {
|
||||
display: block;
|
||||
float: left;
|
||||
list-style-type: none;
|
||||
margin-right: 0.5em;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.nav a {
|
||||
color: #666666;
|
||||
display: block;
|
||||
padding: 0.25em 0.7em;
|
||||
text-decoration: none;
|
||||
-moz-border-radius: 0.3em;
|
||||
-webkit-border-radius: 0.3em;
|
||||
border-radius: 0.3em;
|
||||
}
|
||||
|
||||
.nav li.dropdown-item a {
|
||||
-webkit-border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.nav a:active, .nav a:visited {
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.nav a:focus, .nav a:hover {
|
||||
background-color: #999999;
|
||||
color: #ffffff;
|
||||
outline: none;
|
||||
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.no-borderradius .nav a:focus, .no-borderradius .nav a:hover {
|
||||
background-color: transparent;
|
||||
color: #444444;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.nav a.home, .nav a.list, .nav a.create {
|
||||
background-position: 0.7em center;
|
||||
background-repeat: no-repeat;
|
||||
text-indent: 25px;
|
||||
}
|
||||
|
||||
.nav a.home {
|
||||
background-image: url(../images/skin/house.png);
|
||||
}
|
||||
|
||||
.nav a.list {
|
||||
background-image: url(../images/skin/database_table.png);
|
||||
}
|
||||
|
||||
.nav a.create {
|
||||
background-image: url(../images/skin/database_add.png);
|
||||
}
|
||||
|
||||
.nav li.dropdown.show ul.dropdown-menu {
|
||||
background-color: #424649;
|
||||
}
|
||||
|
||||
/* CREATE/EDIT FORMS AND SHOW PAGES */
|
||||
|
||||
fieldset,
|
||||
.property-list {
|
||||
margin: 0.6em 1.25em 0 1.25em;
|
||||
padding: 0.3em 1.8em 1.25em;
|
||||
position: relative;
|
||||
zoom: 1;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.property-list .fieldcontain {
|
||||
list-style: none;
|
||||
overflow: hidden;
|
||||
zoom: 1;
|
||||
}
|
||||
|
||||
.fieldcontain {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.fieldcontain label,
|
||||
.fieldcontain .property-label {
|
||||
color: #666666;
|
||||
text-align: right;
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
.fieldcontain .property-label {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.fieldcontain .property-value {
|
||||
display: block;
|
||||
margin-left: 27%;
|
||||
}
|
||||
|
||||
label {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
margin: 0 0.25em 0 0;
|
||||
}
|
||||
|
||||
input, select, textarea {
|
||||
background-color: #fcfcfc;
|
||||
border: 1px solid #cccccc;
|
||||
font-size: 1em;
|
||||
padding: 0.2em 0.4em;
|
||||
}
|
||||
|
||||
select {
|
||||
padding: 0.2em 0.2em 0.2em 0;
|
||||
}
|
||||
|
||||
select[multiple] {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 250px;
|
||||
height: 150px;
|
||||
overflow: auto; /* IE always renders vertical scrollbar without this */
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
input[type=checkbox], input[type=radio] {
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
input:focus, select:focus, textarea:focus {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #eeeeee;
|
||||
outline: 0;
|
||||
-moz-box-shadow: 0 0 0.5em #ffffff;
|
||||
-webkit-box-shadow: 0 0 0.5em #ffffff;
|
||||
box-shadow: 0 0 0.5em #ffffff;
|
||||
}
|
||||
|
||||
.required-indicator {
|
||||
color: #cc0000;
|
||||
display: inline-block;
|
||||
font-weight: bold;
|
||||
margin-left: 0.3em;
|
||||
position: relative;
|
||||
top: 0.1em;
|
||||
}
|
||||
|
||||
ul.one-to-many {
|
||||
display: inline-block;
|
||||
list-style-position: inside;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
ul.one-to-many li.add {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
/* EMBEDDED PROPERTIES */
|
||||
|
||||
fieldset.embedded {
|
||||
background-color: transparent;
|
||||
border: 1px solid #CCCCCC;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
-moz-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
fieldset.embedded legend {
|
||||
margin: 0 1em;
|
||||
}
|
||||
|
||||
/* MESSAGES AND ERRORS */
|
||||
|
||||
.errors,
|
||||
.message {
|
||||
font-size: 0.8em;
|
||||
line-height: 2;
|
||||
margin: 1em 2em;
|
||||
padding: 0.25em;
|
||||
}
|
||||
|
||||
.message {
|
||||
background: #f3f3ff;
|
||||
border: 1px solid #b2d1ff;
|
||||
color: #006dba;
|
||||
-moz-box-shadow: 0 0 0.25em #b2d1ff;
|
||||
-webkit-box-shadow: 0 0 0.25em #b2d1ff;
|
||||
box-shadow: 0 0 0.25em #b2d1ff;
|
||||
}
|
||||
|
||||
.errors {
|
||||
background: #fff3f3;
|
||||
border: 1px solid #ffaaaa;
|
||||
color: #cc0000;
|
||||
-moz-box-shadow: 0 0 0.25em #ff8888;
|
||||
-webkit-box-shadow: 0 0 0.25em #ff8888;
|
||||
box-shadow: 0 0 0.25em #ff8888;
|
||||
}
|
||||
|
||||
.errors ul,
|
||||
.message {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.errors li {
|
||||
list-style: none;
|
||||
background: transparent url(../images/skin/exclamation.png) 0.5em 50% no-repeat;
|
||||
text-indent: 2.2em;
|
||||
}
|
||||
|
||||
.message {
|
||||
background: transparent url(../images/skin/information.png) 0.5em 50% no-repeat;
|
||||
text-indent: 2.2em;
|
||||
}
|
||||
|
||||
/* form fields with errors */
|
||||
|
||||
.error input, .error select, .error textarea {
|
||||
background: #fff3f3;
|
||||
border-color: #ffaaaa;
|
||||
color: #cc0000;
|
||||
}
|
||||
|
||||
.error input:focus, .error select:focus, .error textarea:focus {
|
||||
-moz-box-shadow: 0 0 0.5em #ffaaaa;
|
||||
-webkit-box-shadow: 0 0 0.5em #ffaaaa;
|
||||
box-shadow: 0 0 0.5em #ffaaaa;
|
||||
}
|
||||
|
||||
/* same effects for browsers that support HTML5 client-side validation (these have to be specified separately or IE will ignore the entire rule) */
|
||||
|
||||
input:invalid, select:invalid, textarea:invalid {
|
||||
background: #fff3f3;
|
||||
border-color: #ffaaaa;
|
||||
color: #cc0000;
|
||||
}
|
||||
|
||||
input:invalid:focus, select:invalid:focus, textarea:invalid:focus {
|
||||
-moz-box-shadow: 0 0 0.5em #ffaaaa;
|
||||
-webkit-box-shadow: 0 0 0.5em #ffaaaa;
|
||||
box-shadow: 0 0 0.5em #ffaaaa;
|
||||
}
|
||||
|
||||
/* TABLES */
|
||||
|
||||
table {
|
||||
border-top: 1px solid #DFDFDF;
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
tr {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
tr>td:first-child, tr>th:first-child {
|
||||
padding-left: 1.25em;
|
||||
}
|
||||
|
||||
tr>td:last-child, tr>th:last-child {
|
||||
padding-right: 1.25em;
|
||||
}
|
||||
|
||||
td, th {
|
||||
line-height: 1.5em;
|
||||
padding: 0.5em 0.6em;
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #efefef;
|
||||
background-image: -moz-linear-gradient(top, #ffffff, #eaeaea);
|
||||
background-image: -webkit-gradient(linear, left top, left bottom, color-stop(0, #ffffff), color-stop(1, #eaeaea));
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorStr = '#ffffff', EndColorStr = '#eaeaea');
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr='#ffffff', EndColorStr='#eaeaea')";
|
||||
color: #666666;
|
||||
font-weight: bold;
|
||||
line-height: 1.7em;
|
||||
padding: 0.2em 0.6em;
|
||||
}
|
||||
|
||||
thead th {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
th a {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
th a:link, th a:visited {
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
th a:hover, th a:focus {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
th.sortable a {
|
||||
background-position: right;
|
||||
background-repeat: no-repeat;
|
||||
padding-right: 1.1em;
|
||||
}
|
||||
|
||||
th.asc a {
|
||||
background-image: url(../images/skin/sorted_asc.gif);
|
||||
}
|
||||
|
||||
th.desc a {
|
||||
background-image: url(../images/skin/sorted_desc.gif);
|
||||
}
|
||||
|
||||
.odd {
|
||||
background: #f7f7f7;
|
||||
}
|
||||
|
||||
.even {
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
th:hover, tr:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
/* PAGINATION */
|
||||
|
||||
.pagination {
|
||||
border-top: 0;
|
||||
margin: 0.8em 1em 0.3em;
|
||||
padding: 0.3em 0.2em;
|
||||
text-align: center;
|
||||
-moz-box-shadow: 0 0 3px 1px #AAAAAA;
|
||||
-webkit-box-shadow: 0 0 3px 1px #AAAAAA;
|
||||
box-shadow: 0 0 3px 1px #AAAAAA;
|
||||
background-color: #EFEFEF;
|
||||
}
|
||||
|
||||
.pagination a,
|
||||
.pagination .currentStep {
|
||||
color: #666666;
|
||||
display: inline-block;
|
||||
margin: 0 0.1em;
|
||||
padding: 0.25em 0.7em;
|
||||
text-decoration: none;
|
||||
-moz-border-radius: 0.3em;
|
||||
-webkit-border-radius: 0.3em;
|
||||
border-radius: 0.3em;
|
||||
}
|
||||
|
||||
.pagination a:hover, .pagination a:focus,
|
||||
.pagination .currentStep {
|
||||
background-color: #999999;
|
||||
color: #ffffff;
|
||||
outline: none;
|
||||
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.no-borderradius .pagination a:hover, .no-borderradius .pagination a:focus,
|
||||
.no-borderradius .pagination .currentStep {
|
||||
background-color: transparent;
|
||||
color: #444444;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* ACTION BUTTONS */
|
||||
|
||||
.buttons {
|
||||
background-color: #efefef;
|
||||
overflow: hidden;
|
||||
padding: 0.3em;
|
||||
-moz-box-shadow: 0 0 3px 1px #aaaaaa;
|
||||
-webkit-box-shadow: 0 0 3px 1px #aaaaaa;
|
||||
box-shadow: 0 0 3px 1px #aaaaaa;
|
||||
margin: 0.1em 0 0 0;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.buttons input,
|
||||
.buttons a {
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
color: #666666;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
margin: 0 0.25em 0;
|
||||
overflow: visible;
|
||||
padding: 0.25em 0.7em;
|
||||
text-decoration: none;
|
||||
|
||||
-moz-border-radius: 0.3em;
|
||||
-webkit-border-radius: 0.3em;
|
||||
border-radius: 0.3em;
|
||||
}
|
||||
|
||||
.buttons input:hover, .buttons input:focus,
|
||||
.buttons a:hover, .buttons a:focus {
|
||||
background-color: #999999;
|
||||
color: #ffffff;
|
||||
outline: none;
|
||||
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.8);
|
||||
-moz-box-shadow: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.no-borderradius .buttons input:hover, .no-borderradius .buttons input:focus,
|
||||
.no-borderradius .buttons a:hover, .no-borderradius .buttons a:focus {
|
||||
background-color: transparent;
|
||||
color: #444444;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.buttons .delete, .buttons .edit, .buttons .save {
|
||||
background-position: 0.7em center;
|
||||
background-repeat: no-repeat;
|
||||
text-indent: 25px;
|
||||
}
|
||||
|
||||
.buttons .delete {
|
||||
background-image: url(../images/skin/database_delete.png);
|
||||
}
|
||||
|
||||
.buttons .edit {
|
||||
background-image: url(../images/skin/database_edit.png);
|
||||
}
|
||||
|
||||
.buttons .save {
|
||||
background-image: url(../images/skin/database_save.png);
|
||||
}
|
||||
|
||||
a.skip {
|
||||
position: absolute;
|
||||
left: -9999px;
|
||||
}
|
||||
|
||||
.grails-logo-container {
|
||||
background: #7c7c7c no-repeat 50% 30%;
|
||||
margin-bottom: 20px;
|
||||
color: white;
|
||||
height:300px;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
img.grails-logo {
|
||||
height:340px;
|
||||
margin-top:-10px;
|
||||
}
|
82
webui/grails-app/assets/stylesheets/mobile.css
Normal file
@@ -0,0 +1,82 @@
|
||||
/* Styles for mobile devices */
|
||||
|
||||
@media screen and (max-width: 480px) {
|
||||
.nav {
|
||||
padding: 0.5em;
|
||||
}
|
||||
|
||||
.nav li {
|
||||
margin: 0 0.5em 0 0;
|
||||
padding: 0.25em;
|
||||
}
|
||||
|
||||
/* Hide individual steps in pagination, just have next & previous */
|
||||
.pagination .step, .pagination .currentStep {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.pagination .prevLink {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.pagination .nextLink {
|
||||
float: right;
|
||||
}
|
||||
|
||||
/* pagination needs to wrap around floated buttons */
|
||||
.pagination {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* slightly smaller margin around content body */
|
||||
fieldset,
|
||||
.property-list {
|
||||
padding: 0.3em 1em 1em;
|
||||
}
|
||||
|
||||
input, textarea {
|
||||
width: 100%;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
-ms-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
select, input[type=checkbox], input[type=radio], input[type=submit], input[type=button], input[type=reset] {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/* hide all but the first column of list tables */
|
||||
.scaffold-list td:not(:first-child),
|
||||
.scaffold-list th:not(:first-child) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.scaffold-list thead th {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* stack form elements */
|
||||
.fieldcontain {
|
||||
margin-top: 0.6em;
|
||||
}
|
||||
|
||||
.fieldcontain label,
|
||||
.fieldcontain .property-label,
|
||||
.fieldcontain .property-value {
|
||||
display: block;
|
||||
float: none;
|
||||
margin: 0 0 0.25em 0;
|
||||
text-align: left;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.errors ul,
|
||||
.message p {
|
||||
margin: 0.5em;
|
||||
}
|
||||
|
||||
.error ul {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
137
webui/grails-app/conf/application.yml
Normal file
@@ -0,0 +1,137 @@
|
||||
---
|
||||
grails:
|
||||
profile: web
|
||||
codegen:
|
||||
defaultPackage: webui
|
||||
gorm:
|
||||
reactor:
|
||||
# Whether to translate GORM events into Reactor events
|
||||
# Disabled by default for performance reasons
|
||||
events: false
|
||||
info:
|
||||
app:
|
||||
name: '@info.app.name@'
|
||||
version: '@info.app.version@'
|
||||
grailsVersion: '@info.app.grailsVersion@'
|
||||
spring:
|
||||
jmx:
|
||||
unique-names: true
|
||||
main:
|
||||
banner-mode: "off"
|
||||
groovy:
|
||||
template:
|
||||
check-template-location: false
|
||||
devtools:
|
||||
restart:
|
||||
additional-exclude:
|
||||
- '*.gsp'
|
||||
- '**/*.gsp'
|
||||
- '*.gson'
|
||||
- '**/*.gson'
|
||||
- 'logback.groovy'
|
||||
- '*.properties'
|
||||
management:
|
||||
endpoints:
|
||||
enabled-by-default: false
|
||||
|
||||
---
|
||||
grails:
|
||||
mime:
|
||||
disable:
|
||||
accept:
|
||||
header:
|
||||
userAgents:
|
||||
- Gecko
|
||||
- WebKit
|
||||
- Presto
|
||||
- Trident
|
||||
types:
|
||||
all: '*/*'
|
||||
atom: application/atom+xml
|
||||
css: text/css
|
||||
csv: text/csv
|
||||
form: application/x-www-form-urlencoded
|
||||
html:
|
||||
- text/html
|
||||
- application/xhtml+xml
|
||||
js: text/javascript
|
||||
json:
|
||||
- application/json
|
||||
- text/json
|
||||
multipartForm: multipart/form-data
|
||||
pdf: application/pdf
|
||||
rss: application/rss+xml
|
||||
text: text/plain
|
||||
hal:
|
||||
- application/hal+json
|
||||
- application/hal+xml
|
||||
xml:
|
||||
- text/xml
|
||||
- application/xml
|
||||
urlmapping:
|
||||
cache:
|
||||
maxsize: 1000
|
||||
controllers:
|
||||
defaultScope: singleton
|
||||
converters:
|
||||
encoding: UTF-8
|
||||
views:
|
||||
default:
|
||||
codec: html
|
||||
gsp:
|
||||
encoding: UTF-8
|
||||
htmlcodec: xml
|
||||
codecs:
|
||||
expression: html
|
||||
scriptlet: html
|
||||
taglib: none
|
||||
staticparts: none
|
||||
management:
|
||||
endpoints:
|
||||
jmx:
|
||||
unique-names: true
|
||||
|
||||
---
|
||||
hibernate:
|
||||
cache:
|
||||
queries: false
|
||||
use_second_level_cache: false
|
||||
use_query_cache: false
|
||||
dataSource:
|
||||
pooled: true
|
||||
jmxExport: true
|
||||
driverClassName: org.h2.Driver
|
||||
username: sa
|
||||
password: ''
|
||||
|
||||
environments:
|
||||
development:
|
||||
dataSource:
|
||||
dbCreate: create-drop
|
||||
url: jdbc:h2:mem:devDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
|
||||
test:
|
||||
dataSource:
|
||||
dbCreate: update
|
||||
url: jdbc:h2:mem:testDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
|
||||
production:
|
||||
dataSource:
|
||||
dbCreate: none
|
||||
url: jdbc:h2:./prodDb;MVCC=TRUE;LOCK_TIMEOUT=10000;DB_CLOSE_ON_EXIT=FALSE
|
||||
properties:
|
||||
jmxEnabled: true
|
||||
initialSize: 5
|
||||
maxActive: 50
|
||||
minIdle: 5
|
||||
maxIdle: 25
|
||||
maxWait: 10000
|
||||
maxAge: 600000
|
||||
timeBetweenEvictionRunsMillis: 5000
|
||||
minEvictableIdleTimeMillis: 60000
|
||||
validationQuery: SELECT 1
|
||||
validationQueryTimeout: 3
|
||||
validationInterval: 15000
|
||||
testOnBorrow: true
|
||||
testWhileIdle: true
|
||||
testOnReturn: false
|
||||
jdbcInterceptors: ConnectionState
|
||||
defaultTransactionIsolation: 2 # TRANSACTION_READ_COMMITTED
|
37
webui/grails-app/conf/logback.groovy
Normal file
@@ -0,0 +1,37 @@
|
||||
import grails.util.BuildSettings
|
||||
import grails.util.Environment
|
||||
import org.springframework.boot.logging.logback.ColorConverter
|
||||
import org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter
|
||||
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
||||
conversionRule 'clr', ColorConverter
|
||||
conversionRule 'wex', WhitespaceThrowableProxyConverter
|
||||
|
||||
// See http://logback.qos.ch/manual/groovy.html for details on configuration
|
||||
appender('STDOUT', ConsoleAppender) {
|
||||
encoder(PatternLayoutEncoder) {
|
||||
charset = StandardCharsets.UTF_8
|
||||
|
||||
pattern =
|
||||
'%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} ' + // Date
|
||||
'%clr(%5p) ' + // Log level
|
||||
'%clr(---){faint} %clr([%15.15t]){faint} ' + // Thread
|
||||
'%clr(%-40.40logger{39}){cyan} %clr(:){faint} ' + // Logger
|
||||
'%m%n%wex' // Message
|
||||
}
|
||||
}
|
||||
|
||||
def targetDir = BuildSettings.TARGET_DIR
|
||||
if (Environment.isDevelopmentMode() && targetDir != null) {
|
||||
appender("FULL_STACKTRACE", FileAppender) {
|
||||
file = "${targetDir}/stacktrace.log"
|
||||
append = true
|
||||
encoder(PatternLayoutEncoder) {
|
||||
charset = StandardCharsets.UTF_8
|
||||
pattern = "%level %logger - %msg%n"
|
||||
}
|
||||
}
|
||||
logger("StackTrace", ERROR, ['FULL_STACKTRACE'], false)
|
||||
}
|
||||
root(ERROR, ['STDOUT'])
|
3
webui/grails-app/conf/spring/resources.groovy
Normal file
@@ -0,0 +1,3 @@
|
||||
// Place your Spring DSL code here
|
||||
beans = {
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
package webui
|
||||
|
||||
class HelloController {
|
||||
|
||||
def index() {
|
||||
render "Hello from MuWire"
|
||||
}
|
||||
}
|
16
webui/grails-app/controllers/webui/UrlMappings.groovy
Normal file
@@ -0,0 +1,16 @@
|
||||
package webui
|
||||
|
||||
class UrlMappings {
|
||||
|
||||
static mappings = {
|
||||
"/$controller/$action?/$id?(.$format)?"{
|
||||
constraints {
|
||||
// apply constraints here
|
||||
}
|
||||
}
|
||||
|
||||
"/"(view:"/index")
|
||||
"500"(view:'/error')
|
||||
"404"(view:'/notFound')
|
||||
}
|
||||
}
|
56
webui/grails-app/i18n/messages.properties
Normal file
@@ -0,0 +1,56 @@
|
||||
default.doesnt.match.message=Property [{0}] of class [{1}] with value [{2}] does not match the required pattern [{3}]
|
||||
default.invalid.url.message=Property [{0}] of class [{1}] with value [{2}] is not a valid URL
|
||||
default.invalid.creditCard.message=Property [{0}] of class [{1}] with value [{2}] is not a valid credit card number
|
||||
default.invalid.email.message=Property [{0}] of class [{1}] with value [{2}] is not a valid e-mail address
|
||||
default.invalid.range.message=Property [{0}] of class [{1}] with value [{2}] does not fall within the valid range from [{3}] to [{4}]
|
||||
default.invalid.size.message=Property [{0}] of class [{1}] with value [{2}] does not fall within the valid size range from [{3}] to [{4}]
|
||||
default.invalid.max.message=Property [{0}] of class [{1}] with value [{2}] exceeds maximum value [{3}]
|
||||
default.invalid.min.message=Property [{0}] of class [{1}] with value [{2}] is less than minimum value [{3}]
|
||||
default.invalid.max.size.message=Property [{0}] of class [{1}] with value [{2}] exceeds the maximum size of [{3}]
|
||||
default.invalid.min.size.message=Property [{0}] of class [{1}] with value [{2}] is less than the minimum size of [{3}]
|
||||
default.invalid.validator.message=Property [{0}] of class [{1}] with value [{2}] does not pass custom validation
|
||||
default.not.inlist.message=Property [{0}] of class [{1}] with value [{2}] is not contained within the list [{3}]
|
||||
default.blank.message=Property [{0}] of class [{1}] cannot be blank
|
||||
default.not.equal.message=Property [{0}] of class [{1}] with value [{2}] cannot equal [{3}]
|
||||
default.null.message=Property [{0}] of class [{1}] cannot be null
|
||||
default.not.unique.message=Property [{0}] of class [{1}] with value [{2}] must be unique
|
||||
|
||||
default.paginate.prev=Previous
|
||||
default.paginate.next=Next
|
||||
default.boolean.true=True
|
||||
default.boolean.false=False
|
||||
default.date.format=yyyy-MM-dd HH:mm:ss z
|
||||
default.number.format=0
|
||||
|
||||
default.created.message={0} {1} created
|
||||
default.updated.message={0} {1} updated
|
||||
default.deleted.message={0} {1} deleted
|
||||
default.not.deleted.message={0} {1} could not be deleted
|
||||
default.not.found.message={0} not found with id {1}
|
||||
default.optimistic.locking.failure=Another user has updated this {0} while you were editing
|
||||
|
||||
default.home.label=Home
|
||||
default.list.label={0} List
|
||||
default.add.label=Add {0}
|
||||
default.new.label=New {0}
|
||||
default.create.label=Create {0}
|
||||
default.show.label=Show {0}
|
||||
default.edit.label=Edit {0}
|
||||
|
||||
default.button.create.label=Create
|
||||
default.button.edit.label=Edit
|
||||
default.button.update.label=Update
|
||||
default.button.delete.label=Delete
|
||||
default.button.delete.confirm.message=Are you sure?
|
||||
|
||||
# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
|
||||
typeMismatch.java.net.URL=Property {0} must be a valid URL
|
||||
typeMismatch.java.net.URI=Property {0} must be a valid URI
|
||||
typeMismatch.java.util.Date=Property {0} must be a valid Date
|
||||
typeMismatch.java.lang.Double=Property {0} must be a valid number
|
||||
typeMismatch.java.lang.Integer=Property {0} must be a valid number
|
||||
typeMismatch.java.lang.Long=Property {0} must be a valid number
|
||||
typeMismatch.java.lang.Short=Property {0} must be a valid number
|
||||
typeMismatch.java.math.BigDecimal=Property {0} must be a valid number
|
||||
typeMismatch.java.math.BigInteger=Property {0} must be a valid number
|
||||
typeMismatch=Property {0} is type-mismatched
|
55
webui/grails-app/i18n/messages_cs.properties
Normal file
@@ -0,0 +1,55 @@
|
||||
default.doesnt.match.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] neodpovídá požadovanému vzoru [{3}]
|
||||
default.invalid.url.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] není validní URL
|
||||
default.invalid.creditCard.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] není validní číslo kreditní karty
|
||||
default.invalid.email.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] není validní emailová adresa
|
||||
default.invalid.range.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] není v povoleném rozmezí od [{3}] do [{4}]
|
||||
default.invalid.size.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] není v povoleném rozmezí od [{3}] do [{4}]
|
||||
default.invalid.max.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] překračuje maximální povolenou hodnotu [{3}]
|
||||
default.invalid.min.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] je menší než minimální povolená hodnota [{3}]
|
||||
default.invalid.max.size.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] překračuje maximální velikost [{3}]
|
||||
default.invalid.min.size.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] je menší než minimální velikost [{3}]
|
||||
default.invalid.validator.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] neprošla validací
|
||||
default.not.inlist.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] není obsažena v seznamu [{3}]
|
||||
default.blank.message=Položka [{0}] třídy [{1}] nemůže být prázdná
|
||||
default.not.equal.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] nemůže být stejná jako [{3}]
|
||||
default.null.message=Položka [{0}] třídy [{1}] nemůže být prázdná
|
||||
default.not.unique.message=Položka [{0}] třídy [{1}] o hodnotě [{2}] musí být unikátní
|
||||
|
||||
default.paginate.prev=Předcházející
|
||||
default.paginate.next=Následující
|
||||
default.boolean.true=Pravda
|
||||
default.boolean.false=Nepravda
|
||||
default.date.format=dd. MM. yyyy HH:mm:ss z
|
||||
default.number.format=0
|
||||
|
||||
default.created.message={0} {1} vytvořeno
|
||||
default.updated.message={0} {1} aktualizováno
|
||||
default.deleted.message={0} {1} smazáno
|
||||
default.not.deleted.message={0} {1} nelze smazat
|
||||
default.not.found.message={0} nenalezen s id {1}
|
||||
default.optimistic.locking.failure=Jiný uživatel aktualizoval záznam {0}, právě když byl vámi editován
|
||||
|
||||
default.home.label=Domů
|
||||
default.list.label={0} Seznam
|
||||
default.add.label=Přidat {0}
|
||||
default.new.label=Nový {0}
|
||||
default.create.label=Vytvořit {0}
|
||||
default.show.label=Ukázat {0}
|
||||
default.edit.label=Editovat {0}
|
||||
|
||||
default.button.create.label=Vytvoř
|
||||
default.button.edit.label=Edituj
|
||||
default.button.update.label=Aktualizuj
|
||||
default.button.delete.label=Smaž
|
||||
default.button.delete.confirm.message=Jste si jistý?
|
||||
|
||||
# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
|
||||
typeMismatch.java.net.URL=Položka {0} musí být validní URL
|
||||
typeMismatch.java.net.URI=Položka {0} musí být validní URI
|
||||
typeMismatch.java.util.Date=Položka {0} musí být validní datum
|
||||
typeMismatch.java.lang.Double=Položka {0} musí být validní desetinné číslo
|
||||
typeMismatch.java.lang.Integer=Položka {0} musí být validní číslo
|
||||
typeMismatch.java.lang.Long=Položka {0} musí být validní číslo
|
||||
typeMismatch.java.lang.Short=Položka {0} musí být validní číslo
|
||||
typeMismatch.java.math.BigDecimal=Položka {0} musí být validní číslo
|
||||
typeMismatch.java.math.BigInteger=Položka {0} musí být validní číslo
|
56
webui/grails-app/i18n/messages_da.properties
Normal file
@@ -0,0 +1,56 @@
|
||||
default.doesnt.match.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] overholder ikke mønsteret [{3}]
|
||||
default.invalid.url.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] er ikke en gyldig URL
|
||||
default.invalid.creditCard.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] er ikke et gyldigt kreditkortnummer
|
||||
default.invalid.email.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] er ikke en gyldig e-mail adresse
|
||||
default.invalid.range.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] ligger ikke inden for intervallet fra [{3}] til [{4}]
|
||||
default.invalid.size.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] ligger ikke inden for størrelsen fra [{3}] til [{4}]
|
||||
default.invalid.max.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] overstiger den maksimale værdi [{3}]
|
||||
default.invalid.min.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] er under den minimale værdi [{3}]
|
||||
default.invalid.max.size.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] overstiger den maksimale størrelse på [{3}]
|
||||
default.invalid.min.size.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] er under den minimale størrelse på [{3}]
|
||||
default.invalid.validator.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] overholder ikke den brugerdefinerede validering
|
||||
default.not.inlist.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] findes ikke i listen [{3}]
|
||||
default.blank.message=Feltet [{0}] i klassen [{1}] kan ikke være tom
|
||||
default.not.equal.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] må ikke være [{3}]
|
||||
default.null.message=Feltet [{0}] i klassen [{1}] kan ikke være null
|
||||
default.not.unique.message=Feltet [{0}] i klassen [{1}] som har værdien [{2}] skal være unik
|
||||
|
||||
default.paginate.prev=Forrige
|
||||
default.paginate.next=Næste
|
||||
default.boolean.true=Sand
|
||||
default.boolean.false=Falsk
|
||||
default.date.format=yyyy-MM-dd HH:mm:ss z
|
||||
default.number.format=0
|
||||
|
||||
default.created.message={0} {1} oprettet
|
||||
default.updated.message={0} {1} opdateret
|
||||
default.deleted.message={0} {1} slettet
|
||||
default.not.deleted.message={0} {1} kunne ikke slettes
|
||||
default.not.found.message={0} med id {1} er ikke fundet
|
||||
default.optimistic.locking.failure=En anden bruger har opdateret denne {0} imens du har lavet rettelser
|
||||
|
||||
default.home.label=Hjem
|
||||
default.list.label={0} Liste
|
||||
default.add.label=Tilføj {0}
|
||||
default.new.label=Ny {0}
|
||||
default.create.label=Opret {0}
|
||||
default.show.label=Vis {0}
|
||||
default.edit.label=Ret {0}
|
||||
|
||||
default.button.create.label=Opret
|
||||
default.button.edit.label=Ret
|
||||
default.button.update.label=Opdater
|
||||
default.button.delete.label=Slet
|
||||
default.button.delete.confirm.message=Er du sikker?
|
||||
|
||||
# Databindingsfejl. Brug "typeMismatch.$className.$propertyName for at passe til en given klasse (f.eks typeMismatch.Book.author)
|
||||
typeMismatch.java.net.URL=Feltet {0} skal være en valid URL
|
||||
typeMismatch.java.net.URI=Feltet {0} skal være en valid URI
|
||||
typeMismatch.java.util.Date=Feltet {0} skal være en valid Dato
|
||||
typeMismatch.java.lang.Double=Feltet {0} skal være et valid tal
|
||||
typeMismatch.java.lang.Integer=Feltet {0} skal være et valid tal
|
||||
typeMismatch.java.lang.Long=Feltet {0} skal være et valid tal
|
||||
typeMismatch.java.lang.Short=Feltet {0} skal være et valid tal
|
||||
typeMismatch.java.math.BigDecimal=Feltet {0} skal være et valid tal
|
||||
typeMismatch.java.math.BigInteger=Feltet {0} skal være et valid tal
|
||||
|
55
webui/grails-app/i18n/messages_de.properties
Normal file
@@ -0,0 +1,55 @@
|
||||
default.doesnt.match.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] entspricht nicht dem vorgegebenen Muster [{3}]
|
||||
default.invalid.url.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist keine gültige URL
|
||||
default.invalid.creditCard.message=Das Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist keine gültige Kreditkartennummer
|
||||
default.invalid.email.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist keine gültige E-Mail Adresse
|
||||
default.invalid.range.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist nicht im Wertebereich von [{3}] bis [{4}]
|
||||
default.invalid.size.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist nicht im Wertebereich von [{3}] bis [{4}]
|
||||
default.invalid.max.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist größer als der Höchstwert von [{3}]
|
||||
default.invalid.min.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist kleiner als der Mindestwert von [{3}]
|
||||
default.invalid.max.size.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] übersteigt den Höchstwert von [{3}]
|
||||
default.invalid.min.size.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] unterschreitet den Mindestwert von [{3}]
|
||||
default.invalid.validator.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist ungültig
|
||||
default.not.inlist.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] ist nicht in der Liste [{3}] enthalten.
|
||||
default.blank.message=Die Eigenschaft [{0}] des Typs [{1}] darf nicht leer sein
|
||||
default.not.equal.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] darf nicht gleich [{3}] sein
|
||||
default.null.message=Die Eigenschaft [{0}] des Typs [{1}] darf nicht null sein
|
||||
default.not.unique.message=Die Eigenschaft [{0}] des Typs [{1}] mit dem Wert [{2}] darf nur einmal vorkommen
|
||||
|
||||
default.paginate.prev=Vorherige
|
||||
default.paginate.next=Nächste
|
||||
default.boolean.true=Wahr
|
||||
default.boolean.false=Falsch
|
||||
default.date.format=dd.MM.yyyy HH:mm:ss z
|
||||
default.number.format=0
|
||||
|
||||
default.created.message={0} {1} wurde angelegt
|
||||
default.updated.message={0} {1} wurde geändert
|
||||
default.deleted.message={0} {1} wurde gelöscht
|
||||
default.not.deleted.message={0} {1} konnte nicht gelöscht werden
|
||||
default.not.found.message={0} mit der id {1} wurde nicht gefunden
|
||||
default.optimistic.locking.failure=Ein anderer Benutzer hat das {0} Object geändert während Sie es bearbeitet haben
|
||||
|
||||
default.home.label=Home
|
||||
default.list.label={0} Liste
|
||||
default.add.label={0} hinzufügen
|
||||
default.new.label={0} anlegen
|
||||
default.create.label={0} anlegen
|
||||
default.show.label={0} anzeigen
|
||||
default.edit.label={0} bearbeiten
|
||||
|
||||
default.button.create.label=Anlegen
|
||||
default.button.edit.label=Bearbeiten
|
||||
default.button.update.label=Aktualisieren
|
||||
default.button.delete.label=Löschen
|
||||
default.button.delete.confirm.message=Sind Sie sicher?
|
||||
|
||||
# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
|
||||
typeMismatch.java.net.URL=Die Eigenschaft {0} muss eine gültige URL sein
|
||||
typeMismatch.java.net.URI=Die Eigenschaft {0} muss eine gültige URI sein
|
||||
typeMismatch.java.util.Date=Die Eigenschaft {0} muss ein gültiges Datum sein
|
||||
typeMismatch.java.lang.Double=Die Eigenschaft {0} muss eine gültige Zahl sein
|
||||
typeMismatch.java.lang.Integer=Die Eigenschaft {0} muss eine gültige Zahl sein
|
||||
typeMismatch.java.lang.Long=Die Eigenschaft {0} muss eine gültige Zahl sein
|
||||
typeMismatch.java.lang.Short=Die Eigenschaft {0} muss eine gültige Zahl sein
|
||||
typeMismatch.java.math.BigDecimal=Die Eigenschaft {0} muss eine gültige Zahl sein
|
||||
typeMismatch.java.math.BigInteger=Die Eigenschaft {0} muss eine gültige Zahl sein
|
55
webui/grails-app/i18n/messages_es.properties
Normal file
@@ -0,0 +1,55 @@
|
||||
default.doesnt.match.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no corresponde al patrón [{3}]
|
||||
default.invalid.url.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no es una URL válida
|
||||
default.invalid.creditCard.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no es un número de tarjeta de crédito válida
|
||||
default.invalid.email.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no es una dirección de correo electrónico válida
|
||||
default.invalid.range.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no entra en el rango válido de [{3}] a [{4}]
|
||||
default.invalid.size.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no entra en el tamaño válido de [{3}] a [{4}]
|
||||
default.invalid.max.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] excede el valor máximo [{3}]
|
||||
default.invalid.min.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] es menos que el valor mínimo [{3}]
|
||||
default.invalid.max.size.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] excede el tamaño máximo de [{3}]
|
||||
default.invalid.min.size.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] es menor que el tamaño mínimo de [{3}]
|
||||
default.invalid.validator.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no es válido
|
||||
default.not.inlist.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no esta contenido dentro de la lista [{3}]
|
||||
default.blank.message=La propiedad [{0}] de la clase [{1}] no puede ser vacía
|
||||
default.not.equal.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] no puede igualar a [{3}]
|
||||
default.null.message=La propiedad [{0}] de la clase [{1}] no puede ser nulo
|
||||
default.not.unique.message=La propiedad [{0}] de la clase [{1}] con valor [{2}] debe ser única
|
||||
|
||||
default.paginate.prev=Anterior
|
||||
default.paginate.next=Siguiente
|
||||
default.boolean.true=Verdadero
|
||||
default.boolean.false=Falso
|
||||
default.date.format=yyyy-MM-dd HH:mm:ss z
|
||||
default.number.format=0
|
||||
|
||||
default.created.message={0} {1} creado
|
||||
default.updated.message={0} {1} actualizado
|
||||
default.deleted.message={0} {1} eliminado
|
||||
default.not.deleted.message={0} {1} no puede eliminarse
|
||||
default.not.found.message=No se encuentra {0} con id {1}
|
||||
default.optimistic.locking.failure=Mientras usted editaba, otro usuario ha actualizado su {0}
|
||||
|
||||
default.home.label=Principal
|
||||
default.list.label={0} Lista
|
||||
default.add.label=Agregar {0}
|
||||
default.new.label=Nuevo {0}
|
||||
default.create.label=Crear {0}
|
||||
default.show.label=Mostrar {0}
|
||||
default.edit.label=Editar {0}
|
||||
|
||||
default.button.create.label=Crear
|
||||
default.button.edit.label=Editar
|
||||
default.button.update.label=Actualizar
|
||||
default.button.delete.label=Eliminar
|
||||
default.button.delete.confirm.message=¿Está usted seguro?
|
||||
|
||||
# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
|
||||
typeMismatch.java.net.URL=La propiedad {0} debe ser una URL válida
|
||||
typeMismatch.java.net.URI=La propiedad {0} debe ser una URI válida
|
||||
typeMismatch.java.util.Date=La propiedad {0} debe ser una fecha válida
|
||||
typeMismatch.java.lang.Double=La propiedad {0} debe ser un número válido
|
||||
typeMismatch.java.lang.Integer=La propiedad {0} debe ser un número válido
|
||||
typeMismatch.java.lang.Long=La propiedad {0} debe ser un número válido
|
||||
typeMismatch.java.lang.Short=La propiedad {0} debe ser un número válido
|
||||
typeMismatch.java.math.BigDecimal=La propiedad {0} debe ser un número válido
|
||||
typeMismatch.java.math.BigInteger=La propiedad {0} debe ser un número válido
|
19
webui/grails-app/i18n/messages_fr.properties
Normal file
@@ -0,0 +1,19 @@
|
||||
default.doesnt.match.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] ne correspond pas au pattern [{3}]
|
||||
default.invalid.url.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] n'est pas une URL valide
|
||||
default.invalid.creditCard.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] n'est pas un numéro de carte de crédit valide
|
||||
default.invalid.email.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] n'est pas une adresse e-mail valide
|
||||
default.invalid.range.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] n'est pas contenue dans l'intervalle [{3}] à [{4}]
|
||||
default.invalid.size.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] n'est pas contenue dans l'intervalle [{3}] à [{4}]
|
||||
default.invalid.max.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] est supérieure à la valeur maximum [{3}]
|
||||
default.invalid.min.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] est inférieure à la valeur minimum [{3}]
|
||||
default.invalid.max.size.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] est supérieure à la valeur maximum [{3}]
|
||||
default.invalid.min.size.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] est inférieure à la valeur minimum [{3}]
|
||||
default.invalid.validator.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] n'est pas valide
|
||||
default.not.inlist.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] ne fait pas partie de la liste [{3}]
|
||||
default.blank.message=La propriété [{0}] de la classe [{1}] ne peut pas être vide
|
||||
default.not.equal.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] ne peut pas être égale à [{3}]
|
||||
default.null.message=La propriété [{0}] de la classe [{1}] ne peut pas être nulle
|
||||
default.not.unique.message=La propriété [{0}] de la classe [{1}] avec la valeur [{2}] doit être unique
|
||||
|
||||
default.paginate.prev=Précédent
|
||||
default.paginate.next=Suivant
|
55
webui/grails-app/i18n/messages_it.properties
Normal file
@@ -0,0 +1,55 @@
|
||||
default.doesnt.match.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non corrisponde al pattern [{3}]
|
||||
default.invalid.url.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non è un URL valido
|
||||
default.invalid.creditCard.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non è un numero di carta di credito valido
|
||||
default.invalid.email.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non è un indirizzo email valido
|
||||
default.invalid.range.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non rientra nell'intervallo valido da [{3}] a [{4}]
|
||||
default.invalid.size.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non rientra nell'intervallo di dimensioni valide da [{3}] a [{4}]
|
||||
default.invalid.max.message=La proprietà [{0}] della classe [{1}] con valore [{2}] è maggiore di [{3}]
|
||||
default.invalid.min.message=La proprietà [{0}] della classe [{1}] con valore [{2}] è minore di [{3}]
|
||||
default.invalid.max.size.message=La proprietà [{0}] della classe [{1}] con valore [{2}] è maggiore di [{3}]
|
||||
default.invalid.min.size.message=La proprietà [{0}] della classe [{1}] con valore [{2}] è minore di [{3}]
|
||||
default.invalid.validator.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non è valida
|
||||
default.not.inlist.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non è contenuta nella lista [{3}]
|
||||
default.blank.message=La proprietà [{0}] della classe [{1}] non può essere vuota
|
||||
default.not.equal.message=La proprietà [{0}] della classe [{1}] con valore [{2}] non può essere uguale a [{3}]
|
||||
default.null.message=La proprietà [{0}] della classe [{1}] non può essere null
|
||||
default.not.unique.message=La proprietà [{0}] della classe [{1}] con valore [{2}] deve essere unica
|
||||
|
||||
default.paginate.prev=Precedente
|
||||
default.paginate.next=Successivo
|
||||
default.boolean.true=Vero
|
||||
default.boolean.false=Falso
|
||||
default.date.format=dd/MM/yyyy HH:mm:ss z
|
||||
default.number.format=0
|
||||
|
||||
default.created.message={0} {1} creato
|
||||
default.updated.message={0} {1} aggiornato
|
||||
default.deleted.message={0} {1} eliminato
|
||||
default.not.deleted.message={0} {1} non può essere eliminato
|
||||
default.not.found.message={0} non trovato con id {1}
|
||||
default.optimistic.locking.failure=Un altro utente ha aggiornato questo {0} mentre si era in modifica
|
||||
|
||||
default.home.label=Home
|
||||
default.list.label={0} Elenco
|
||||
default.add.label=Aggiungi {0}
|
||||
default.new.label=Nuovo {0}
|
||||
default.create.label=Crea {0}
|
||||
default.show.label=Mostra {0}
|
||||
default.edit.label=Modifica {0}
|
||||
|
||||
default.button.create.label=Crea
|
||||
default.button.edit.label=Modifica
|
||||
default.button.update.label=Aggiorna
|
||||
default.button.delete.label=Elimina
|
||||
default.button.delete.confirm.message=Si è sicuri?
|
||||
|
||||
# Data binding errors. Usa "typeMismatch.$className.$propertyName per la personalizzazione (es typeMismatch.Book.author)
|
||||
typeMismatch.java.net.URL=La proprietà {0} deve essere un URL valido
|
||||
typeMismatch.java.net.URI=La proprietà {0} deve essere un URI valido
|
||||
typeMismatch.java.util.Date=La proprietà {0} deve essere una data valida
|
||||
typeMismatch.java.lang.Double=La proprietà {0} deve essere un numero valido
|
||||
typeMismatch.java.lang.Integer=La proprietà {0} deve essere un numero valido
|
||||
typeMismatch.java.lang.Long=La proprietà {0} deve essere un numero valido
|
||||
typeMismatch.java.lang.Short=La proprietà {0} deve essere un numero valido
|
||||
typeMismatch.java.math.BigDecimal=La proprietà {0} deve essere un numero valido
|
||||
typeMismatch.java.math.BigInteger=La proprietà {0} deve essere un numero valido
|
55
webui/grails-app/i18n/messages_ja.properties
Normal file
@@ -0,0 +1,55 @@
|
||||
default.doesnt.match.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、[{3}]パターンと一致していません。
|
||||
default.invalid.url.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、有効なURLではありません。
|
||||
default.invalid.creditCard.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、有効なクレジットカード番号ではありません。
|
||||
default.invalid.email.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、有効なメールアドレスではありません。
|
||||
default.invalid.range.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、[{3}]から[{4}]範囲内を指定してください。
|
||||
default.invalid.size.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、[{3}]から[{4}]以内を指定してください。
|
||||
default.invalid.max.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、最大値[{3}]より大きいです。
|
||||
default.invalid.min.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、最小値[{3}]より小さいです。
|
||||
default.invalid.max.size.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、最大値[{3}]より大きいです。
|
||||
default.invalid.min.size.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、最小値[{3}]より小さいです。
|
||||
default.invalid.validator.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、カスタムバリデーションを通過できません。
|
||||
default.not.inlist.message=クラス[{1}]プロパティ[{0}]の値[{2}]は、[{3}]リスト内に存在しません。
|
||||
default.blank.message=[{1}]クラスのプロパティ[{0}]の空白は許可されません。
|
||||
default.not.equal.message=クラス[{1}]プロパティ[{0}]の値[{2}]に[{3}]は許可されません。
|
||||
default.null.message=[{1}]クラスのプロパティ[{0}]にnullは許可されません。
|
||||
default.not.unique.message=クラス[{1}]プロパティ[{0}]の値[{2}]は既に使用されています。
|
||||
|
||||
default.paginate.prev=戻る
|
||||
default.paginate.next=次へ
|
||||
default.boolean.true=はい
|
||||
default.boolean.false=いいえ
|
||||
default.date.format=yyyy/MM/dd HH:mm:ss z
|
||||
default.number.format=0
|
||||
|
||||
default.created.message={0}(id:{1})を作成しました。
|
||||
default.updated.message={0}(id:{1})を更新しました。
|
||||
default.deleted.message={0}(id:{1})を削除しました。
|
||||
default.not.deleted.message={0}(id:{1})は削除できませんでした。
|
||||
default.not.found.message={0}(id:{1})は見つかりませんでした。
|
||||
default.optimistic.locking.failure=この{0}は編集中に他のユーザによって先に更新されています。
|
||||
|
||||
default.home.label=ホーム
|
||||
default.list.label={0}リスト
|
||||
default.add.label={0}を追加
|
||||
default.new.label={0}を新規作成
|
||||
default.create.label={0}を作成
|
||||
default.show.label={0}詳細
|
||||
default.edit.label={0}を編集
|
||||
|
||||
default.button.create.label=作成
|
||||
default.button.edit.label=編集
|
||||
default.button.update.label=更新
|
||||
default.button.delete.label=削除
|
||||
default.button.delete.confirm.message=本当に削除してよろしいですか?
|
||||
|
||||
# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
|
||||
typeMismatch.java.net.URL={0}は有効なURLでなければなりません。
|
||||
typeMismatch.java.net.URI={0}は有効なURIでなければなりません。
|
||||
typeMismatch.java.util.Date={0}は有効な日付でなければなりません。
|
||||
typeMismatch.java.lang.Double={0}は有効な数値でなければなりません。
|
||||
typeMismatch.java.lang.Integer={0}は有効な数値でなければなりません。
|
||||
typeMismatch.java.lang.Long={0}は有効な数値でなければなりません。
|
||||
typeMismatch.java.lang.Short={0}は有効な数値でなければなりません。
|
||||
typeMismatch.java.math.BigDecimal={0}は有効な数値でなければなりません。
|
||||
typeMismatch.java.math.BigInteger={0}は有効な数値でなければなりません。
|
56
webui/grails-app/i18n/messages_nb.properties
Normal file
@@ -0,0 +1,56 @@
|
||||
default.doesnt.match.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] overholder ikke mønsteret [{3}]
|
||||
default.invalid.url.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] er ikke en gyldig URL
|
||||
default.invalid.creditCard.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] er ikke et gyldig kredittkortnummer
|
||||
default.invalid.email.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] er ikke en gyldig epostadresse
|
||||
default.invalid.range.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] er ikke innenfor intervallet [{3}] til [{4}]
|
||||
default.invalid.size.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] er ikke innenfor intervallet [{3}] til [{4}]
|
||||
default.invalid.max.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] overstiger maksimumsverdien på [{3}]
|
||||
default.invalid.min.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] er under minimumsverdien på [{3}]
|
||||
default.invalid.max.size.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] overstiger maksimumslengden på [{3}]
|
||||
default.invalid.min.size.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] er kortere enn minimumslengden på [{3}]
|
||||
default.invalid.validator.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] overholder ikke den brukerdefinerte valideringen
|
||||
default.not.inlist.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] finnes ikke i listen [{3}]
|
||||
default.blank.message=Feltet [{0}] i klassen [{1}] kan ikke være tom
|
||||
default.not.equal.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] kan ikke være [{3}]
|
||||
default.null.message=Feltet [{0}] i klassen [{1}] kan ikke være null
|
||||
default.not.unique.message=Feltet [{0}] i klassen [{1}] med verdien [{2}] må være unik
|
||||
|
||||
default.paginate.prev=Forrige
|
||||
default.paginate.next=Neste
|
||||
default.boolean.true=Ja
|
||||
default.boolean.false=Nei
|
||||
default.date.format=dd.MM.yyyy HH:mm:ss z
|
||||
default.number.format=0
|
||||
|
||||
default.created.message={0} {1} opprettet
|
||||
default.updated.message={0} {1} oppdatert
|
||||
default.deleted.message={0} {1} slettet
|
||||
default.not.deleted.message={0} {1} kunne ikke slettes
|
||||
default.not.found.message={0} med id {1} ble ikke funnet
|
||||
default.optimistic.locking.failure=En annen bruker har oppdatert denne {0} mens du redigerte
|
||||
|
||||
default.home.label=Hjem
|
||||
default.list.label={0}liste
|
||||
default.add.label=Legg til {0}
|
||||
default.new.label=Ny {0}
|
||||
default.create.label=Opprett {0}
|
||||
default.show.label=Vis {0}
|
||||
default.edit.label=Endre {0}
|
||||
|
||||
default.button.create.label=Opprett
|
||||
default.button.edit.label=Endre
|
||||
default.button.update.label=Oppdater
|
||||
default.button.delete.label=Slett
|
||||
default.button.delete.confirm.message=Er du sikker?
|
||||
|
||||
# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
|
||||
typeMismatch.java.net.URL=Feltet {0} må være en gyldig URL
|
||||
typeMismatch.java.net.URI=Feltet {0} må være en gyldig URI
|
||||
typeMismatch.java.util.Date=Feltet {0} må være en gyldig dato
|
||||
typeMismatch.java.lang.Double=Feltet {0} må være et gyldig tall
|
||||
typeMismatch.java.lang.Integer=Feltet {0} må være et gyldig heltall
|
||||
typeMismatch.java.lang.Long=Feltet {0} må være et gyldig heltall
|
||||
typeMismatch.java.lang.Short=Feltet {0} må være et gyldig heltall
|
||||
typeMismatch.java.math.BigDecimal=Feltet {0} må være et gyldig tall
|
||||
typeMismatch.java.math.BigInteger=Feltet {0} må være et gyldig heltall
|
||||
|
55
webui/grails-app/i18n/messages_nl.properties
Normal file
@@ -0,0 +1,55 @@
|
||||
default.doesnt.match.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] komt niet overeen met het vereiste patroon [{3}]
|
||||
default.invalid.url.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] is geen geldige URL
|
||||
default.invalid.creditCard.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] is geen geldig credit card nummer
|
||||
default.invalid.email.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] is geen geldig e-mailadres
|
||||
default.invalid.range.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] valt niet in de geldige waardenreeks van [{3}] tot [{4}]
|
||||
default.invalid.size.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] valt niet in de geldige grootte van [{3}] tot [{4}]
|
||||
default.invalid.max.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] overschrijdt de maximumwaarde [{3}]
|
||||
default.invalid.min.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] is minder dan de minimumwaarde [{3}]
|
||||
default.invalid.max.size.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] overschrijdt de maximumgrootte van [{3}]
|
||||
default.invalid.min.size.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] is minder dan minimumgrootte van [{3}]
|
||||
default.invalid.validator.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] is niet geldig
|
||||
default.not.inlist.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] komt niet voor in de lijst [{3}]
|
||||
default.blank.message=Attribuut [{0}] van entiteit [{1}] mag niet leeg zijn
|
||||
default.not.equal.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] mag niet gelijk zijn aan [{3}]
|
||||
default.null.message=Attribuut [{0}] van entiteit [{1}] mag niet leeg zijn
|
||||
default.not.unique.message=Attribuut [{0}] van entiteit [{1}] met waarde [{2}] moet uniek zijn
|
||||
|
||||
default.paginate.prev=Vorige
|
||||
default.paginate.next=Volgende
|
||||
default.boolean.true=Ja
|
||||
default.boolean.false=Nee
|
||||
default.date.format=dd-MM-yyyy HH:mm:ss z
|
||||
default.number.format=0
|
||||
|
||||
default.created.message={0} {1} ingevoerd
|
||||
default.updated.message={0} {1} gewijzigd
|
||||
default.deleted.message={0} {1} verwijderd
|
||||
default.not.deleted.message={0} {1} kon niet worden verwijderd
|
||||
default.not.found.message={0} met id {1} kon niet worden gevonden
|
||||
default.optimistic.locking.failure=Een andere gebruiker heeft deze {0} al gewijzigd
|
||||
|
||||
default.home.label=Home
|
||||
default.list.label={0} Overzicht
|
||||
default.add.label=Toevoegen {0}
|
||||
default.new.label=Invoeren {0}
|
||||
default.create.label=Invoeren {0}
|
||||
default.show.label=Details {0}
|
||||
default.edit.label=Wijzigen {0}
|
||||
|
||||
default.button.create.label=Invoeren
|
||||
default.button.edit.label=Wijzigen
|
||||
default.button.update.label=Opslaan
|
||||
default.button.delete.label=Verwijderen
|
||||
default.button.delete.confirm.message=Weet je het zeker?
|
||||
|
||||
# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
|
||||
typeMismatch.java.net.URL=Attribuut {0} is geen geldige URL
|
||||
typeMismatch.java.net.URI=Attribuut {0} is geen geldige URI
|
||||
typeMismatch.java.util.Date=Attribuut {0} is geen geldige datum
|
||||
typeMismatch.java.lang.Double=Attribuut {0} is geen geldig nummer
|
||||
typeMismatch.java.lang.Integer=Attribuut {0} is geen geldig nummer
|
||||
typeMismatch.java.lang.Long=Attribuut {0} is geen geldig nummer
|
||||
typeMismatch.java.lang.Short=Attribuut {0} is geen geldig nummer
|
||||
typeMismatch.java.math.BigDecimal=Attribuut {0} is geen geldig nummer
|
||||
typeMismatch.java.math.BigInteger=Attribuut {0} is geen geldig nummer
|
59
webui/grails-app/i18n/messages_pl.properties
Normal file
@@ -0,0 +1,59 @@
|
||||
#
|
||||
# Translated by Matthias Hryniszak - padcom@gmail.com
|
||||
#
|
||||
|
||||
default.doesnt.match.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] nie pasuje do wymaganego wzorca [{3}]
|
||||
default.invalid.url.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] jest niepoprawnym adresem URL
|
||||
default.invalid.creditCard.message=Właściwość [{0}] klasy [{1}] with value [{2}] nie jest poprawnym numerem karty kredytowej
|
||||
default.invalid.email.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] nie jest poprawnym adresem e-mail
|
||||
default.invalid.range.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] nie zawiera się zakładanym zakresie od [{3}] do [{4}]
|
||||
default.invalid.size.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] nie zawiera się w zakładanym zakresie rozmiarów od [{3}] do [{4}]
|
||||
default.invalid.max.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] przekracza maksymalną wartość [{3}]
|
||||
default.invalid.min.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] jest mniejsza niż minimalna wartość [{3}]
|
||||
default.invalid.max.size.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] przekracza maksymalny rozmiar [{3}]
|
||||
default.invalid.min.size.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] jest mniejsza niż minimalny rozmiar [{3}]
|
||||
default.invalid.validator.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] nie spełnia założonych niestandardowych warunków
|
||||
default.not.inlist.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] nie zawiera się w liście [{3}]
|
||||
default.blank.message=Właściwość [{0}] klasy [{1}] nie może być pusta
|
||||
default.not.equal.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] nie może równać się [{3}]
|
||||
default.null.message=Właściwość [{0}] klasy [{1}] nie może być null
|
||||
default.not.unique.message=Właściwość [{0}] klasy [{1}] o wartości [{2}] musi być unikalna
|
||||
|
||||
default.paginate.prev=Poprzedni
|
||||
default.paginate.next=Następny
|
||||
default.boolean.true=Prawda
|
||||
default.boolean.false=Fałsz
|
||||
default.date.format=yyyy-MM-dd HH:mm:ss z
|
||||
default.number.format=0
|
||||
|
||||
default.created.message=Utworzono {0} {1}
|
||||
default.updated.message=Zaktualizowano {0} {1}
|
||||
default.deleted.message=Usunięto {0} {1}
|
||||
default.not.deleted.message={0} {1} nie mógł zostać usunięty
|
||||
default.not.found.message=Nie znaleziono {0} o id {1}
|
||||
default.optimistic.locking.failure=Inny użytkownik zaktualizował ten obiekt {0} w trakcie twoich zmian
|
||||
|
||||
default.home.label=Strona domowa
|
||||
default.list.label=Lista {0}
|
||||
default.add.label=Dodaj {0}
|
||||
default.new.label=Utwórz {0}
|
||||
default.create.label=Utwórz {0}
|
||||
default.show.label=Pokaż {0}
|
||||
default.edit.label=Edytuj {0}
|
||||
|
||||
default.button.create.label=Utwórz
|
||||
default.button.edit.label=Edytuj
|
||||
default.button.update.label=Zaktualizuj
|
||||
default.button.delete.label=Usuń
|
||||
default.button.delete.confirm.message=Czy jesteś pewien?
|
||||
|
||||
# Data binding errors. Use "typeMismatch.$className.$propertyName to customize (eg typeMismatch.Book.author)
|
||||
typeMismatch.java.net.URL=Właściwość {0} musi być poprawnym adresem URL
|
||||
typeMismatch.java.net.URI=Właściwość {0} musi być poprawnym adresem URI
|
||||
typeMismatch.java.util.Date=Właściwość {0} musi być poprawną datą
|
||||
typeMismatch.java.lang.Double=Właściwość {0} musi być poprawnyą liczbą
|
||||
typeMismatch.java.lang.Integer=Właściwość {0} musi być poprawnyą liczbą
|
||||
typeMismatch.java.lang.Long=Właściwość {0} musi być poprawnyą liczbą
|
||||
typeMismatch.java.lang.Short=Właściwość {0} musi być poprawnyą liczbą
|
||||
typeMismatch.java.math.BigDecimal=Właściwość {0} musi być poprawnyą liczbą
|
||||
typeMismatch.java.math.BigInteger=Właściwość {0} musi być poprawnyą liczbą
|
59
webui/grails-app/i18n/messages_pt_BR.properties
Normal file
@@ -0,0 +1,59 @@
|
||||
#
|
||||
# Translated by Lucas Teixeira - lucastex@gmail.com
|
||||
#
|
||||
|
||||
default.doesnt.match.message=O campo [{0}] da classe [{1}] com o valor [{2}] não atende ao padrão definido [{3}]
|
||||
default.invalid.url.message=O campo [{0}] da classe [{1}] com o valor [{2}] não é uma URL válida
|
||||
default.invalid.creditCard.message=O campo [{0}] da classe [{1}] com o valor [{2}] não é um número válido de cartão de crédito
|
||||
default.invalid.email.message=O campo [{0}] da classe [{1}] com o valor [{2}] não é um endereço de email válido.
|
||||
default.invalid.range.message=O campo [{0}] da classe [{1}] com o valor [{2}] não está entre a faixa de valores válida de [{3}] até [{4}]
|
||||
default.invalid.size.message=O campo [{0}] da classe [{1}] com o valor [{2}] não está na faixa de tamanho válida de [{3}] até [{4}]
|
||||
default.invalid.max.message=O campo [{0}] da classe [{1}] com o valor [{2}] ultrapassa o valor máximo [{3}]
|
||||
default.invalid.min.message=O campo [{0}] da classe [{1}] com o valor [{2}] não atinge o valor mínimo [{3}]
|
||||
default.invalid.max.size.message=O campo [{0}] da classe [{1}] com o valor [{2}] ultrapassa o tamanho máximo de [{3}]
|
||||
default.invalid.min.size.message=O campo [{0}] da classe [{1}] com o valor [{2}] não atinge o tamanho mínimo de [{3}]
|
||||
default.invalid.validator.message=O campo [{0}] da classe [{1}] com o valor [{2}] não passou na validação
|
||||
default.not.inlist.message=O campo [{0}] da classe [{1}] com o valor [{2}] não é um valor dentre os permitidos na lista [{3}]
|
||||
default.blank.message=O campo [{0}] da classe [{1}] não pode ficar em branco
|
||||
default.not.equal.message=O campo [{0}] da classe [{1}] com o valor [{2}] não pode ser igual a [{3}]
|
||||
default.null.message=O campo [{0}] da classe [{1}] não pode ser vazio
|
||||
default.not.unique.message=O campo [{0}] da classe [{1}] com o valor [{2}] deve ser único
|
||||
|
||||
default.paginate.prev=Anterior
|
||||
default.paginate.next=Próximo
|
||||
default.boolean.true=Sim
|
||||
default.boolean.false=Não
|
||||
default.date.format=dd/MM/yyyy HH:mm:ss z
|
||||
default.number.format=0
|
||||
|
||||
default.created.message={0} {1} criado
|
||||
default.updated.message={0} {1} atualizado
|
||||
default.deleted.message={0} {1} removido
|
||||
default.not.deleted.message={0} {1} não pode ser removido
|
||||
default.not.found.message={0} não foi encontrado com o id {1}
|
||||
default.optimistic.locking.failure=Outro usuário atualizou este [{0}] enquanto você tentou salvá-lo
|
||||
|
||||
default.home.label=Principal
|
||||
default.list.label={0} Listagem
|
||||
default.add.label=Adicionar {0}
|
||||
default.new.label=Novo {0}
|
||||
default.create.label=Criar {0}
|
||||
default.show.label=Ver {0}
|
||||
default.edit.label=Editar {0}
|
||||
|
||||
default.button.create.label=Criar
|
||||
default.button.edit.label=Editar
|
||||
default.button.update.label=Alterar
|
||||
default.button.delete.label=Remover
|
||||
default.button.delete.confirm.message=Tem certeza?
|
||||
|
||||
# Mensagens de erro em atribuição de valores. Use "typeMismatch.$className.$propertyName" para customizar (eg typeMismatch.Book.author)
|
||||
typeMismatch.java.net.URL=O campo {0} deve ser uma URL válida.
|
||||
typeMismatch.java.net.URI=O campo {0} deve ser uma URI válida.
|
||||
typeMismatch.java.util.Date=O campo {0} deve ser uma data válida
|
||||
typeMismatch.java.lang.Double=O campo {0} deve ser um número válido.
|
||||
typeMismatch.java.lang.Integer=O campo {0} deve ser um número válido.
|
||||
typeMismatch.java.lang.Long=O campo {0} deve ser um número válido.
|
||||
typeMismatch.java.lang.Short=O campo {0} deve ser um número válido.
|
||||
typeMismatch.java.math.BigDecimal=O campo {0} deve ser um número válido.
|
||||
typeMismatch.java.math.BigInteger=O campo {0} deve ser um número válido.
|
34
webui/grails-app/i18n/messages_pt_PT.properties
Normal file
@@ -0,0 +1,34 @@
|
||||
#
|
||||
# translation by miguel.ping@gmail.com, based on pt_BR translation by Lucas Teixeira - lucastex@gmail.com
|
||||
#
|
||||
|
||||
default.doesnt.match.message=O campo [{0}] da classe [{1}] com o valor [{2}] não corresponde ao padrão definido [{3}]
|
||||
default.invalid.url.message=O campo [{0}] da classe [{1}] com o valor [{2}] não é um URL válido
|
||||
default.invalid.creditCard.message=O campo [{0}] da classe [{1}] com o valor [{2}] não é um número válido de cartão de crédito
|
||||
default.invalid.email.message=O campo [{0}] da classe [{1}] com o valor [{2}] não é um endereço de email válido.
|
||||
default.invalid.range.message=O campo [{0}] da classe [{1}] com o valor [{2}] não está dentro dos limites de valores válidos de [{3}] a [{4}]
|
||||
default.invalid.size.message=O campo [{0}] da classe [{1}] com o valor [{2}] está fora dos limites de tamanho válido de [{3}] a [{4}]
|
||||
default.invalid.max.message=O campo [{0}] da classe [{1}] com o valor [{2}] ultrapassa o valor máximo [{3}]
|
||||
default.invalid.min.message=O campo [{0}] da classe [{1}] com o valor [{2}] não atinge o valor mínimo [{3}]
|
||||
default.invalid.max.size.message=O campo [{0}] da classe [{1}] com o valor [{2}] ultrapassa o tamanho máximo de [{3}]
|
||||
default.invalid.min.size.message=O campo [{0}] da classe [{1}] com o valor [{2}] não atinge o tamanho mínimo de [{3}]
|
||||
default.invalid.validator.message=O campo [{0}] da classe [{1}] com o valor [{2}] não passou na validação
|
||||
default.not.inlist.message=O campo [{0}] da classe [{1}] com o valor [{2}] não se encontra nos valores permitidos da lista [{3}]
|
||||
default.blank.message=O campo [{0}] da classe [{1}] não pode ser vazio
|
||||
default.not.equal.message=O campo [{0}] da classe [{1}] com o valor [{2}] não pode ser igual a [{3}]
|
||||
default.null.message=O campo [{0}] da classe [{1}] não pode ser vazio
|
||||
default.not.unique.message=O campo [{0}] da classe [{1}] com o valor [{2}] deve ser único
|
||||
|
||||
default.paginate.prev=Anterior
|
||||
default.paginate.next=Próximo
|
||||
|
||||
# Mensagens de erro em atribuição de valores. Use "typeMismatch.$className.$propertyName" para personalizar(eg typeMismatch.Book.author)
|
||||
typeMismatch.java.net.URL=O campo {0} deve ser um URL válido.
|
||||
typeMismatch.java.net.URI=O campo {0} deve ser um URI válido.
|
||||
typeMismatch.java.util.Date=O campo {0} deve ser uma data válida
|
||||
typeMismatch.java.lang.Double=O campo {0} deve ser um número válido.
|
||||
typeMismatch.java.lang.Integer=O campo {0} deve ser um número válido.
|
||||
typeMismatch.java.lang.Long=O campo {0} deve ser um número valido.
|
||||
typeMismatch.java.lang.Short=O campo {0} deve ser um número válido.
|
||||
typeMismatch.java.math.BigDecimal=O campo {0} deve ser um número válido.
|
||||
typeMismatch.java.math.BigInteger=O campo {0} deve ser um número válido.
|