diff --git a/.classpath b/.classpath index 6fc97e48f..ebace4e59 100644 --- a/.classpath +++ b/.classpath @@ -100,6 +100,5 @@ - diff --git a/addon/YaCy.app/Contents/Info.plist b/addon/YaCy.app/Contents/Info.plist index 69804aa9f..5f772f193 100644 --- a/addon/YaCy.app/Contents/Info.plist +++ b/addon/YaCy.app/Contents/Info.plist @@ -19,7 +19,7 @@ CFBundleAllowMixedLocalizations true CFBundleExecutable -startYACY.sh +startYACYMacOS.sh CFBundleDevelopmentRegion English CFBundlePackageType diff --git a/addon/YaCy.app/Contents/MacOS/startYACYMacOS.sh b/addon/YaCy.app/Contents/MacOS/startYACYMacOS.sh new file mode 100755 index 000000000..692ec913f --- /dev/null +++ b/addon/YaCy.app/Contents/MacOS/startYACYMacOS.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env sh + +# Launcher for YaCy in a MacOS bundle : +# rely on the generic startYACY.sh, but specifies the user home relative path for YaCy data +# This data directory is set in conforming to OS X File System Programming Guide +# see : https://developer.apple.com/library/ios/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/MacOSXDirectories/MacOSXDirectories.html + +"`dirname $0`"/startYACY.sh -s "'Library/Application Support/net.yacy.YaCy'" diff --git a/build.xml b/build.xml index faeb82bc0..14acd7b66 100644 --- a/build.xml +++ b/build.xml @@ -242,7 +242,6 @@ - @@ -765,7 +764,8 @@ - + + diff --git a/docker/Dockerfile b/docker/Dockerfile index 35102e4d6..401d1f1d0 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -18,6 +18,10 @@ WORKDIR /opt # - Compile with ant # - remove unnecessary and size consuming .git directory # - remove ant and git packages + +# Possible alternative : copy directly your current sources an remove git clone command from the following RUN +# COPY . /opt/yacy_search_server/ + RUN apt-get update && \ apt-get install -yq ant git && \ git clone https://github.com/yacy/yacy_search_server.git && \ @@ -30,14 +34,17 @@ RUN apt-get update && \ # Set initial admin password : "docker" (encoded with custom yacy md5 function net.yacy.cora.order.Digest.encodeMD5Hex()) RUN sed -i "/adminAccountBase64MD5=/c\adminAccountBase64MD5=MD5:e672161ffdce91be4678605f4f4e6786" /opt/yacy_search_server/defaults/yacy.init +# Intially enable HTTPS : this is the most secure option for remote administrator authentication +RUN sed -i "/server.https=false/c\server.https=true" /opt/yacy_search_server/defaults/yacy.init + # Create user and group yacy : this user will be used to run YaCy main process RUN adduser --system --group --no-create-home --disabled-password yacy # Set ownership of yacy install directory to yacy user/group RUN chown yacy:yacy -R /opt/yacy_search_server -# Expose port 8090 -EXPOSE 8090 +# Expose HTTP and HTTPS default ports +EXPOSE 8090 8443 # Set data volume : yacy data and configuration will persist aven after container stop or destruction VOLUME ["/opt/yacy_search_server/DATA"] diff --git a/docker/Dockerfile.alpine b/docker/Dockerfile.alpine index 497dd70ed..77134f222 100755 --- a/docker/Dockerfile.alpine +++ b/docker/Dockerfile.alpine @@ -51,6 +51,10 @@ WORKDIR /opt # - compile with apache ant # - remove unnecessary and size consuming .git directory # - delete git package and ant binary install + +# Possible alternative : copy directly your current sources an remove git clone command from the following RUN +# COPY . /opt/yacy_search_server/ + RUN apk update && \ apk add --no-cache git && \ git clone https://github.com/yacy/yacy_search_server.git && \ @@ -62,14 +66,17 @@ RUN apk update && \ # Set initial admin password : "docker" (encoded with custom yacy md5 function net.yacy.cora.order.Digest.encodeMD5Hex()) RUN sed -i "/adminAccountBase64MD5=/c\adminAccountBase64MD5=MD5:e672161ffdce91be4678605f4f4e6786" /opt/yacy_search_server/defaults/yacy.init +# Intially enable HTTPS : this is the most secure option for remote administrator authentication +RUN sed -i "/server.https=false/c\server.https=true" /opt/yacy_search_server/defaults/yacy.init + # Create user and group yacy : this user will be used to run YaCy main process RUN addgroup yacy && adduser -S -G yacy -H -D yacy # Set ownership of yacy install directory to yacy user/group RUN chown yacy:yacy -R /opt/yacy_search_server -# Expose port 8090 -EXPOSE 8090 +# Expose HTTP and HTTPS default ports +EXPOSE 8090 8443 # Set data volume : yacy data and configuration will persist aven after container stop or destruction VOLUME ["/opt/yacy_search_server/DATA"] diff --git a/docker/Readme.md b/docker/Readme.md index 0ff35757f..9c9d7c07e 100755 --- a/docker/Readme.md +++ b/docker/Readme.md @@ -18,6 +18,11 @@ Using yacy_search_server/docker/Dockerfile : cd yacy_search_server/docker docker build . +To build the Alpine variant : + + cd yacy_search_server/docker + docker build -f Dockerfile.alpine . + ## Image variants `luccioman/yacy:latest` @@ -49,12 +54,12 @@ You can retrieve the container IP address with `docker inspect`. #### Easier to handle - docker run --name yacy -p 8090:8090 --log-opt max-size=100m --log-opt max-file=2 luccioman/yacy + docker run --name yacy -p 8090:8090 -p 8443:8443 --log-opt max-size=200m --log-opt max-file=2 luccioman/yacy ##### Options detail * --name : allow easier management of your container (without it, docker automatically generate a new name at each startup). -* -p : map host port and container port, allowing web interface access through the usual http://localhost:8090. +* -p 8090:8090 -p 8443:8443 : map host ports to YaCy container ports, allowing web interface access through the usual http://localhost:8090 and https://localhost:8443 (you can set a different mapping, for example -p 443:8443 if you prefer to use the default HTTPS port on your host) * --log-opt max-size : limit maximum docker log file size for this container * --log-opt max-file : limit number of docker rotated log files for this container @@ -76,9 +81,47 @@ Note that you can list all docker volumes with : docker volume ls -#### As background process +#### Start as background process docker run -d luccioman/yacy + +### HTTPS support + +This images are default configured with HTTPS enabled, and use a default certificate stored in defaults/freeworldKeystore. You should use your own certificate. In order to do it, you can proceed as follow. + +#### Self-signed certificate + +A self-signed certificate will provide encrypted communications with your YaCy server, but browsers will still complain about an invalid security certificate with the error "SEC_ERROR_UNKNOWN_ISSUER". If it is sufficient for you, you can permanently add and exception to your browser. + +This kind of certificate can be generated and added to your YaCy Docker container with the following : + + keytool -keystore /var/lib/docker/volumes/[your_yacy_volume]/_data/SETTINGS/yacykeystore -genkey -keyalg RSA -alias yacycert + +Then edit YaCy config file. For example with the nano text editor : + + nano /var/lib/docker/volumes/[your_yacy_volume]/_data/SETTINGS/yacy.conf + +And configure the keyStoreXXXX properties accordingly : + + keyStore=/opt/yacy_search_server/DATA/SETTINGS/yacykeystore + keyStorePassword=yourpassword + +#### Import an existing certificate: + +Importing a certificate validated by a certification authority (CA) will ensure you have full HTTPS support with no security errors when accessing your YaCy peer. You can import an existing certificate in pkcs12 format. + +First copy it to the YaCy Docker container volume : + + cp [yourStore].pkcs12 /var/lib/docker/volumes/[your_yacy_volume]/_data/SETTINGS/[yourStore].pkcs12 + +Then edit YaCy config file. For example with the nano text editor : + + nano /var/lib/docker/volumes/[your_yacy_volume]/_data/SETTINGS/yacy.conf + +And configure the pkcs12XXX properties accordingly : + + pkcs12ImportFile=/opt/yacy_search_server/DATA/SETTINGS/[yourStore].pkcs12 + pkcs12ImportPwd=yourpassword ### Next starts @@ -109,7 +152,7 @@ OR Create new container based on pulled image, using volume data from old container : - docker create --name [tmp-container_name] -p 8090:8090 --volumes-from=[container_name] --log-opt max-size=100m --log-opt max-file=2 luccioman/yacy:latest + docker create --name [tmp-container_name] -p 8090:8090 -p 8443:8443 --volumes-from=[container_name] --log-opt max-size=100m --log-opt max-file=2 luccioman/yacy:latest Stop old container : diff --git a/docker/docker-cloud.yml b/docker/docker-cloud.yml index 24a09a69b..97e965bd7 100644 --- a/docker/docker-cloud.yml +++ b/docker/docker-cloud.yml @@ -2,5 +2,6 @@ yacy: image: 'luccioman/yacy:latest' ports: - '8090:8090' + - '8443:8443' restart: on-failure autoredeploy: true \ No newline at end of file diff --git a/htroot/DictionaryLoader_p.html b/htroot/DictionaryLoader_p.html index 95446429b..a37d8647f 100644 --- a/htroot/DictionaryLoader_p.html +++ b/htroot/DictionaryLoader_p.html @@ -231,6 +231,16 @@
Action
#(syn1Status)#::#(/syn1Status)#
+ +

Russian Thesaurus

+

The data was converted to the YaCy synonym file format and part of the YaCy distribution.

+ +
+
+
#(syn2Status)#
Deactivated
::
Activated
#(/syn2Status)#
+
Action
+
#(syn2Status)#::#(/syn2Status)#
+
#%env/templates/footer.template%# diff --git a/htroot/DictionaryLoader_p.java b/htroot/DictionaryLoader_p.java index 58e4feafb..9b455abaf 100644 --- a/htroot/DictionaryLoader_p.java +++ b/htroot/DictionaryLoader_p.java @@ -45,10 +45,12 @@ public class DictionaryLoader_p { final serverObjects prop = new serverObjects(); // return variable that accumulates replacements final File synonyms_path = new File(sb.dictionariesPath, LibraryProvider.path_to_synonym_dictionaries); - final File synonym_de_default = new File(new File(new File(sb.appPath, "addon"), "synonyms"), "openthesaurus_de_yacy"); + final File synonym_de_default = new File(sb.appPath, "addon/synonyms/openthesaurus_de_yacy"); final File synonym_de_production = new File(synonyms_path, synonym_de_default.getName()); - final File synonym_en_default = new File(new File(new File(sb.appPath, "addon"), "synonyms"), "mobythesaurus_en_yacy"); + final File synonym_en_default = new File(sb.appPath, "addon/synonyms/mobythesaurus_en_yacy"); final File synonym_en_production = new File(synonyms_path, synonym_en_default.getName()); + final File synonym_ru_default = new File(sb.appPath, "addon/synonyms/thesaurus_ru_yacy"); + final File synonym_ru_production = new File(synonyms_path, synonym_ru_default.getName()); /* * distinguish the following cases: * - dictionary file was not loaded -> actions: load the file @@ -70,7 +72,7 @@ public class DictionaryLoader_p { // check here only if there is no possibility synonym libraries have been activated/deactivated prop.put("syn0Status", synonym_de_production.exists() ? 1 : 0); prop.put("syn1Status", synonym_en_production.exists() ? 1 : 0); - + prop.put("syn2Status", synonym_ru_production.exists() ? 1 : 0); return prop; } @@ -322,11 +324,25 @@ public class DictionaryLoader_p { } SynonymLibrary.init(synonyms_path); } - + + if (post.containsKey("syn2Deactivate")) { + synonym_ru_production.delete(); + SynonymLibrary.init(synonyms_path); + } + + if (post.containsKey("syn2Activate")) { + try { + FileUtils.copy(new FileInputStream(synonym_ru_default), synonym_ru_production); + } catch (IOException e) { + ConcurrentLog.logException(e); + } + SynonymLibrary.init(synonyms_path); + } if (post != null) { // check here if there is a possibility synonym libraries have been activated/deactivated prop.put("syn0Status", synonym_de_production.exists() ? 1 : 0); prop.put("syn1Status", synonym_en_production.exists() ? 1 : 0); + prop.put("syn2Status", synonym_ru_production.exists() ? 1 : 0); } // check status again diff --git a/htroot/News.html b/htroot/News.html index 1c2b83507..320717d3d 100644 --- a/htroot/News.html +++ b/htroot/News.html @@ -43,6 +43,10 @@ A change in the personal profile will create a news entry. You can see recently made changes of profile entries on the Network page, where that profile change is visualized with a '*' beside the 'P' (profile) - selector. +
  • + Publishing of added or modified translation for the user interface. Other peers may include it in their local translation list. + To publish a translation, use the integrated translation editor to add a translation and publish it afterwards. +
  • More news services will follow. diff --git a/htroot/Supporter.java b/htroot/Supporter.java index 391a0c9a5..02f44197b 100644 --- a/htroot/Supporter.java +++ b/htroot/Supporter.java @@ -38,6 +38,7 @@ import net.yacy.cora.order.NaturalOrder; import net.yacy.cora.protocol.RequestHeader; import net.yacy.cora.sorting.ConcurrentScoreMap; import net.yacy.cora.sorting.ScoreMap; +import net.yacy.cora.util.ConcurrentLog; import net.yacy.kelondro.index.Row; import net.yacy.kelondro.index.Row.Entry; import net.yacy.peers.NewsDB; @@ -258,7 +259,7 @@ public class Supporter { urlhash = null; } if (urlhash==null) { - System.out.println("Supporter: bad url '" + url + "' from news record " + record.toString()); + ConcurrentLog.info("Supporter", "bad url '" + url + "' from news record " + record.toString()); continue; } if ((vote = negativeHashes.get(urlhash)) != null) { diff --git a/htroot/TransNews_p.html b/htroot/TransNews_p.html new file mode 100644 index 000000000..7e69deedb --- /dev/null +++ b/htroot/TransNews_p.html @@ -0,0 +1,62 @@ + + + + YaCy '#[clientname]#': Translation News + #%env/templates/metas.template%# + + + #%env/templates/header.template%# + #%env/templates/submenuComputation.template%# +

    Translation News for Language #[currentlang]#

    + +
    +

    + You can share your local addition to translations and distribute it to other peers. + The remote peer can vote on your translation and add it to the own local translation.
    + (#[transsize]# entries available)   +   You can check your outgoing messages here +

    +
    + #(errmsg)#::

    Please activate a different language here

    #(/errmsg)# + #{results}# + + +
    + + + + + + + + #(existing)#:: + + + + #(/existing)# + + + + + + + +
    File:#[filename]#Originator
    English:#[source]#
    existing#[target]#
    Translation:#[target]##[peername]#
    score #[score]#  + + +    + + +   Vote on this translation. If you vote positive the translation is added to your local translation list. +
    +
    + + + #{/results}# +

    +
    +

    + + #%env/templates/footer.template%# + + diff --git a/htroot/TransNews_p.java b/htroot/TransNews_p.java new file mode 100644 index 000000000..98c432afd --- /dev/null +++ b/htroot/TransNews_p.java @@ -0,0 +1,323 @@ +// TransNews_p.java +// +// This is a part of YaCy, a peer-to-peer based web search engine +// published on http://yacy.net +// +// This file is contributed by Burkhard Buelte +// +// $LastChangedDate$ +// $LastChangedRevision$ +// $LastChangedBy$ +// +// LICENSE +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation; either version 2 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import net.yacy.cora.protocol.RequestHeader; +import net.yacy.cora.sorting.ConcurrentScoreMap; +import net.yacy.cora.sorting.ScoreMap; +import net.yacy.cora.util.SpaceExceededException; +import net.yacy.peers.NewsDB; +import net.yacy.peers.NewsPool; +import net.yacy.search.Switchboard; +import net.yacy.server.serverObjects; +import net.yacy.server.serverSwitch; +import net.yacy.utils.crypt; +import net.yacy.utils.translation.TranslationManager; + +public class TransNews_p { + + public static serverObjects respond(final RequestHeader header, final serverObjects post, final serverSwitch env) { + final Switchboard sb = (Switchboard) env; + final serverObjects prop = new serverObjects(); + + String currentlang = sb.getConfig("locale.language", "default"); + prop.put("currentlang", currentlang); + + if ("default".equals(currentlang) || "browser".equals(currentlang)) { + prop.put("errmsg", 1); // msg: activate diff lng + prop.put("transsize", 0); + return prop; + } else { + prop.put("errmsg", 0); + } + + TranslationManager transMgr = new TranslationManager(); + File locallangFile = transMgr.getScratchFile(new File(currentlang + ".lng")); + Map> localTrans = transMgr.loadTranslationsLists(locallangFile); + // calculate size of local translations list + int size = 0; + for (Map lst : localTrans.values()) { + size += lst.size(); + } + prop.put("transsize", size); + + + // read voting + if ((post != null) && post.containsKey("publishtranslation")) { + Iterator filenameit = localTrans.keySet().iterator(); + while (filenameit.hasNext()) { + String file = filenameit.next(); + Map tmptrans = localTrans.get(file); + for (String sourcetxt : tmptrans.keySet()) { + String targettxt = tmptrans.get(sourcetxt); + if (targettxt != null && !targettxt.isEmpty()) { + boolean sendit = true; + // check if already published (in newsPool) + Iterator it = sb.peers.newsPool.recordIterator(NewsPool.INCOMING_DB); + while (it.hasNext()) { + NewsDB.Record rtmp = it.next(); + if (rtmp == null) { + continue; + } + if (NewsPool.CATEGORY_TRANSLATION_ADD.equals(rtmp.category())) { + String tmplng = rtmp.attribute("language", null); + String tmpfile = rtmp.attribute("file", null); + String tmpsource = rtmp.attribute("source", null); + //String tmptarget = rtmp.attribute("target", null); + + // if news with file and source exist (maybe from other peer) - skip sending another msg (to avoid confusion) + if ((tmplng != null && tmplng.equals(currentlang)) && (tmpfile != null && tmpfile.equals(file)) + && (tmpsource != null && tmpsource.equals(sourcetxt))) { + sendit = false; + break; + } + + } + } + if (sendit) { + final HashMap map = new HashMap(); + map.put("language", currentlang); + map.put("file", file); + map.put("source", sourcetxt); + map.put("target", targettxt); + sb.peers.newsPool.publishMyNews(sb.peers.mySeed(), NewsPool.CATEGORY_TRANSLATION_ADD, map); + } + } + } + } + } + String refid; + if ((post != null) && ((refid = post.get("voteNegative", null)) != null)) { + + // make new news message with voting + if (!sb.isRobinsonMode()) { + final HashMap map = new HashMap(); + map.put("language", currentlang); + map.put("file", crypt.simpleDecode(post.get("filename", ""))); + map.put("source", crypt.simpleDecode(post.get("source", ""))); + map.put("target", crypt.simpleDecode(post.get("target", ""))); + map.put("vote", "negative"); + map.put("refid", refid); + sb.peers.newsPool.publishMyNews(sb.peers.mySeed(), NewsPool.CATEGORY_TRANSLATION_VOTE_ADD, map); + try { + sb.peers.newsPool.moveOff(NewsPool.INCOMING_DB, refid); + } catch (IOException | SpaceExceededException ex) { + } + } + } + + if ((post != null) && ((refid = post.get("votePositive", null)) != null)) { + + final String filename = post.get("filename"); + + File lngfile = new File(sb.getAppPath("locale.source", "locales"), currentlang + ".lng"); + transMgr = new TranslationManager(lngfile); // load full language for check if entry is new (globally) + if (transMgr.addTranslation(filename, post.get("source"), post.get("target"))) { + // add to local translation extension + transMgr.addTranslation(localTrans, filename, post.get("source"), post.get("target")); + transMgr.saveAsLngFile(currentlang, locallangFile, localTrans); // save local-trans to local-file + transMgr.translateFile(filename); // ad-hoc translate file with new/added text + } // TODO: shall we post voting if translation is not new ? + + // make new news message with voting + final HashMap map = new HashMap(); + map.put("language", currentlang); + map.put("file", crypt.simpleDecode(filename)); + map.put("source", crypt.simpleDecode(post.get("source", ""))); + map.put("target", crypt.simpleDecode(post.get("target", ""))); + map.put("vote", "positive"); + map.put("refid", refid); + sb.peers.newsPool.publishMyNews(sb.peers.mySeed(), NewsPool.CATEGORY_TRANSLATION_VOTE_ADD, map); + try { + sb.peers.newsPool.moveOff(NewsPool.INCOMING_DB, refid); + } catch (IOException | SpaceExceededException ex) { + } + + } + + // create Translation voting list + final HashMap negativeHashes = new HashMap(); // a mapping from an url hash to Integer (count of votes) + final HashMap positiveHashes = new HashMap(); // a mapping from an url hash to Integer (count of votes) + accumulateVotes(sb, negativeHashes, positiveHashes, NewsPool.INCOMING_DB); + final ScoreMap ranking = new ConcurrentScoreMap(); // score cluster for url hashes + final HashMap translation = new HashMap(); // a mapping from an url hash to a kelondroRow.Entry with display properties + accumulateTranslations(sb, translation, ranking, negativeHashes, positiveHashes, NewsPool.INCOMING_DB); + + // read out translation-news array and create property entries + final Iterator k = ranking.keys(false); + int i = 0; + NewsDB.Record row; + String filename; + String source; + String target; + + while (k.hasNext()) { + + refid = k.next(); + if (refid == null) { + continue; + } + + row = translation.get(refid); + if (row == null) { + continue; + } + + String lang = row.attribute("language", null); + filename = row.attribute("file", null); + source = row.attribute("source", null); + target = row.attribute("target", null); + if ((lang == null) || (filename == null) || (source == null) || (target == null)) { + continue; + } + + if (!lang.equals(currentlang)) continue; + + String existingtarget = null; //transMgr.getTranslation(filename, source); + Map tmpMap = localTrans.get(filename); + if (tmpMap != null) { + existingtarget = tmpMap.get(source); + } + + boolean altexist = existingtarget != null && !target.isEmpty() && !existingtarget.isEmpty() && !existingtarget.equals(target); + + prop.put("results_" + i + "_refid", refid); + prop.put("results_" + i + "_url", filename); // url to local file + prop.put("results_" + i + "_targetlanguage", lang); + prop.put("results_" + i + "_filename", filename); + prop.putHTML("results_" + i + "_source", source); + prop.putHTML("results_" + i + "_target", target); + prop.put("results_" + i + "_existing", altexist); + prop.putHTML("results_" + i + "_existing_target", existingtarget); + prop.put("results_" + i + "_score", ranking.get(refid)); + prop.put("results_" + i + "_peername", sb.peers.get(row.originator()).getName()); + i++; + + if (i >= 50) { + break; + } + } + prop.put("results", i); + + return prop; + } + + private static void accumulateVotes(final Switchboard sb, final HashMap negativeHashes, final HashMap positiveHashes, final int dbtype) { + final int maxCount = Math.min(1000, sb.peers.newsPool.size(dbtype)); + NewsDB.Record newsrecord; + final Iterator recordIterator = sb.peers.newsPool.recordIterator(dbtype); + int j = 0; + while ((recordIterator.hasNext()) && (j++ < maxCount)) { + newsrecord = recordIterator.next(); + if (newsrecord == null) { + continue; + } + + if (newsrecord.category().equals(NewsPool.CATEGORY_TRANSLATION_VOTE_ADD)) { + final String refid = newsrecord.attribute("refid", ""); + final String vote = newsrecord.attribute("vote", ""); + final int factor = ((dbtype == NewsPool.OUTGOING_DB) || (dbtype == NewsPool.PUBLISHED_DB)) ? 2 : 1; + if (vote.equals("negative")) { + final Integer i = negativeHashes.get(refid); + if (i == null) { + negativeHashes.put(refid, Integer.valueOf(factor)); + } else { + negativeHashes.put(refid, Integer.valueOf(i.intValue() + factor)); + } + } + if (vote.equals("positive")) { + final Integer i = positiveHashes.get(refid); + if (i == null) { + positiveHashes.put(refid, Integer.valueOf(factor)); + } else { + positiveHashes.put(refid, Integer.valueOf(i.intValue() + factor)); + } + } + } + } + } + + private static void accumulateTranslations( + final Switchboard sb, + final HashMap translationmsg, final ScoreMap ranking, + final HashMap negativeHashes, final HashMap positiveHashes, final int dbtype) { + final int maxCount = Math.min(1000, sb.peers.newsPool.size(dbtype)); + NewsDB.Record newsrecord; + final Iterator recordIterator = sb.peers.newsPool.recordIterator(dbtype); + int j = 0; + String refid = ""; + String targetlanguage =""; + String filename=""; + String source=""; + String target=""; + + int score = 0; + Integer vote; + + while ((recordIterator.hasNext()) && (j++ < maxCount)) { + newsrecord = recordIterator.next(); + if (newsrecord == null) { + continue; + } + + if ((newsrecord.category().equals(NewsPool.CATEGORY_TRANSLATION_ADD)) + && ((sb.peers.get(newsrecord.originator())) != null)) { + refid = newsrecord.id(); + targetlanguage = newsrecord.attribute("language", ""); + filename = newsrecord.attribute("file", ""); + source = newsrecord.attribute("source", ""); + target = newsrecord.attribute("target", ""); + if (refid.isEmpty() || targetlanguage.isEmpty() || filename.isEmpty() || source.isEmpty() || target.isEmpty()) { + continue; + } + score = 0; + } + + // add/subtract votes and write record + + if ((vote = negativeHashes.get(refid)) != null) { + score -= vote.intValue(); + } + if ((vote = positiveHashes.get(refid)) != null) { + score += vote.intValue(); + } + // consider double-entries + if (translationmsg.containsKey(refid)) { + ranking.inc(refid, score); + } else { + ranking.set(refid, score); + translationmsg.put(refid, newsrecord); + } + + } + } +} diff --git a/htroot/Translator_p.html b/htroot/Translator_p.html index 131af2999..1025e6bc0 100644 --- a/htroot/Translator_p.html +++ b/htroot/Translator_p.html @@ -17,7 +17,7 @@ -

    Target Language: #[targetlang]#

    #[errmsg]#

    +

    Target Language: #[targetlang]#

    #(errmsg)#::

    activate a different language here

    #(/errmsg)#