Changeset addc9c5


Ignore:
Timestamp:
Sep 15, 2015 1:33:29 PM (4 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
37597b8
Parents:
a2e3850
Message:

News: connect it all together (ticket #1425):

  • Enable new NewsManager? to load/store feed items on disk by UUID
  • News items are stored forever, not lost when they are removed from feed
  • News read in once at startup, not at every summary bar refresh
  • Convert old initialNews.xml and news.xml to NewsEntry? format
  • Limit display to 2 news items in summary bar, /home and /console
  • New /news page to show all news
Files:
11 edited

Legend:

Unmodified
Added
Removed
  • apps/routerconsole/java/src/net/i2p/router/news/NewsManager.java

    ra2e3850 raddc9c5  
    55import java.io.IOException;
    66import java.io.Reader;
     7import java.text.DateFormat;
     8import java.text.ParseException;
    79import java.util.ArrayList;
    810import java.util.Collections;
    911import java.util.Date;
    1012import java.util.List;
     13import java.util.TimeZone;
    1114
    1215import net.i2p.I2PAppContext;
     
    3538    private volatile ClientAppState _state = UNINITIALIZED;
    3639    private List<NewsEntry> _currentNews;
    37     private NewsMetadata _currentMetadata;
     40    // TODO
     41    // Metadata is persisted in the old news.xml format by
     42    // NewsFetcher.outputOldNewsXML() and read in at startup by
     43    // ConsoleUpdateManager.startup() and NewsFetcher.checkForUpdates().
     44    // While running, the UpdateManager keeps the metadata.
     45    // NewsHelper looks at the news.xml timestamp.
     46    //private NewsMetadata _currentMetadata;
    3847
    3948    public static final String APP_NAME = "news";
     
    94103            if (id == null)
    95104                continue;
     105            String title = e.title;
    96106            boolean found = false;
    97107            for (int i = 0; i < _currentNews.size(); i++) {
    98108                NewsEntry old = _currentNews.get(i);
    99                 if (id.equals(old.id)) {
     109                // try to prevent dups with those created from old news.xml,
     110                // where the UUID is the title
     111                if (id.equals(old.id) || (title != null && title.equals(old.id))) {
    100112                    _currentNews.set(i, e);
    101113                    found = true;
     
    157169        if (newsContent == null || newsContent.equals(""))
    158170            return Collections.emptyList();
    159         return parseNews(newsContent);
     171        return parseNews(newsContent, false);
    160172    }
    161173
     
    172184                out.append(buf, 0, len);
    173185            }
    174             List<NewsEntry> rv = parseNews(out.toString());
     186            List<NewsEntry> rv = parseNews(out.toString(), true);
    175187            if (!rv.isEmpty()) {
    176188                rv.get(0).updated = RFC3339Date.parse3339Date("2015-01-01");
     
    192204    }
    193205
    194     private List<NewsEntry> parseNews(String newsContent) {
     206    /**
     207     *  Used for initialNews.xml and news.xml
     208     *
     209     *  @param addMissingDiv true for initialNews, false for news.xml
     210     */
     211    private List<NewsEntry> parseNews(String newsContent, boolean addMissingDiv) {
    195212        List<NewsEntry> rv = new ArrayList<NewsEntry>();
    196213        // Parse news content for headings.
     
    206223                newsContent.substring(start + 4, start + 6).equals("20") &&
    207224                newsContent.substring(start + 14, start + 16).equals(": ")) {
     225                // initialNews.xml, or old news.xml from server
    208226                entry.updated = RFC3339Date.parse3339Date(newsContent.substring(start + 4, start + 14));
    209227                newsContent = newsContent.substring(start+16);
    210228            } else {
    211229                newsContent = newsContent.substring(start+4);
     230                int colon = newsContent.indexOf(": ");
     231                if (colon > 0 && colon <= 10) {
     232                    //  Parse the format we wrote it out in, in NewsFetcher.outputOldNewsXML()
     233                    //  Doesn't work if the date has a : in it, but SHORT hopefully does not
     234                    DateFormat fmt = DateFormat.getDateInstance(DateFormat.SHORT);
     235                    // the router sets the JVM time zone to UTC but saves the original here so we can get it
     236                    String systemTimeZone = _context.getProperty("i2p.systemTimeZone");
     237                    if (systemTimeZone != null)
     238                        fmt.setTimeZone(TimeZone.getTimeZone(systemTimeZone));
     239                    try {
     240                        Date date = fmt.parse(newsContent.substring(0, colon));
     241                        entry.updated = date.getTime();
     242                        newsContent = newsContent.substring(colon + 2);
     243                    } catch (ParseException pe) {
     244                        // can't find date, will be zero
     245                    }
     246                }
    212247            }
    213248            int end = newsContent.indexOf("</h3>");
     
    223258                else
    224259                    entry.content = newsContent;
     260                // initialNews.xml has the <div> before the <h3>, not after, so we lose it...
     261                // add it back.
     262                if (addMissingDiv)
     263                    entry.content = "<div>\n" + entry.content;
    225264                rv.add(entry);
    226265                start = end;
  • apps/routerconsole/java/src/net/i2p/router/news/NewsXMLParser.java

    ra2e3850 raddc9c5  
    9898     *  @param file XML content only. Any su3 or gunzip handling must have
    9999     *              already happened.
     100     *  @return the root node
    100101     *  @throws IOException on any parse error
    101102     */
    102     public void parse(File file) throws IOException {
    103         parse(new BufferedInputStream(new FileInputStream(file)));
     103    public Node parse(File file) throws IOException {
     104        return parse(new BufferedInputStream(new FileInputStream(file)));
    104105    }
    105106
     
    109110     *  @param in XML content only. Any su3 or gunzip handling must have
    110111     *            already happened.
     112     *  @return the root node
    111113     *  @throws IOException on any parse error
    112114     */
    113     public void parse(InputStream in) throws IOException {
     115    public Node parse(InputStream in) throws IOException {
    114116        _entries = null;
    115117        _metadata = null;
     
    118120            Node root = parser.parse(in);
    119121            extract(root);
     122            return root;
    120123        } catch (ParserException pe) {
    121124            throw new I2PParserException(pe);
     
    353356     *  @return non-null
    354357     */
    355     static List<Node> getNodes(Node node, String name) {
     358    public static List<Node> getNodes(Node node, String name) {
    356359        List<Node> rv = new ArrayList<Node>();
    357360        int count = node.getNNodes();
  • apps/routerconsole/java/src/net/i2p/router/news/PersistNews.java

    ra2e3850 raddc9c5  
    3636class PersistNews {
    3737
    38     private static final String DIR = "docs/news";
     38    private static final String DIR = "docs/feed/news";
    3939    private static final String PFX = "news-";
    4040    private static final String SFX = ".xml.gz";
     
    208208
    209209    /**
     210     *  Unused for now, as we don't have any way to remember it's deleted.
     211     *
    210212     *  @return success
    211213     */
  • apps/routerconsole/java/src/net/i2p/router/update/NewsFetcher.java

    ra2e3850 raddc9c5  
    2222import java.util.TimeZone;
    2323
     24import net.i2p.app.ClientAppManager;
    2425import net.i2p.crypto.SU3File;
    2526import net.i2p.crypto.TrustedUpdate;
     
    2829import net.i2p.router.RouterVersion;
    2930import net.i2p.router.news.NewsEntry;
     31import net.i2p.router.news.NewsManager;
    3032import net.i2p.router.news.NewsMetadata;
    3133import net.i2p.router.news.NewsXMLParser;
     
    4547import net.i2p.util.Translate;
    4648import net.i2p.util.VersionComparator;
     49
     50import org.cybergarage.xml.Node;
    4751
    4852/**
     
    476480            }
    477481            NewsXMLParser parser = new NewsXMLParser(_context);
    478             parser.parse(xml);
     482            Node root = parser.parse(xml);
    479483            xml.delete();
    480484            NewsMetadata data = parser.getMetadata();
    481485            List<NewsEntry> entries = parser.getEntries();
     486            // add entries to the news manager
     487            ClientAppManager cmgr = _context.clientAppManager();
     488            if (cmgr != null) {
     489                NewsManager nmgr = (NewsManager) cmgr.getRegisteredApp(NewsManager.APP_NAME);
     490                if (nmgr != null) {
     491                    nmgr.addEntries(entries);
     492                    List<Node> nodes = NewsXMLParser.getNodes(root, "entry");
     493                    nmgr.storeEntries(nodes);
     494                }
     495            }
     496            // store entries and metadata in old news.xml format
    482497            String sudVersion = su3.getVersionString();
    483498            String signingKeyName = su3.getSignerString();
  • apps/routerconsole/java/src/net/i2p/router/web/NewsFeedHelper.java

    ra2e3850 raddc9c5  
    2121   
    2222    private int _start = 0;
    23     private int _limit = 3;
     23    private int _limit = 2;
    2424
    2525    /**
     
    6363                if (i++ < start)
    6464                    continue;
    65                 buf.append("<h3>");
     65                buf.append("<div class=\"newsentry\"><h3>");
    6666                if (entry.updated > 0) {
    6767                    Date date = new Date(entry.updated);
     
    7070                }
    7171                buf.append(entry.title)
    72                    .append("</h3>\n")
     72                   .append("</h3>\n<div class=\"newscontent\">\n")
    7373                   .append(entry.content)
    74                    .append("\n");
     74                   .append("\n</div></div>\n");
    7575                if (i >= start + max)
    7676                    break;
  • apps/routerconsole/java/src/net/i2p/router/web/NewsHelper.java

    ra2e3850 raddc9c5  
    205205    }
    206206
    207     private static final String BUNDLE_NAME = "net.i2p.router.news.messages";
    208 
    209207    /**
    210208     *  If we haven't downloaded news yet, use the translated initial news file
     
    212210    @Override
    213211    public String getContent() {
    214         File news = new File(_page);
    215         if (!news.exists()) {
    216             _page = (new File(_context.getBaseDir(), "docs/initialNews/initialNews.xml")).getAbsolutePath();
    217             // don't use super, translate on-the-fly
    218             Reader reader = null;
    219             try {
    220                 char[] buf = new char[512];
    221                 StringBuilder out = new StringBuilder(2048);
    222                 reader = new TranslateReader(_context, BUNDLE_NAME, new FileInputStream(_page));
    223                 int len;
    224                 while((len = reader.read(buf)) > 0) {
    225                     out.append(buf, 0, len);
    226                 }
    227                 return out.toString();
    228             } catch (IOException ioe) {
    229                 return "";
    230             } finally {
    231                 try {
    232                     if (reader != null)
    233                         reader.close();
    234                 } catch (IOException foo) {}
    235             }
    236         }
    237         return super.getContent();
     212        return NewsFeedHelper.getEntries(_context, 0, 2);
    238213    }
    239214
     
    313288                    .append(Messages.getString("Show news", ctx));
    314289             }
    315              buf.append("</a>");
     290             buf.append("</a>" +
     291                        " - <a href=\"/news\">")
     292                .append(Messages.getString("Show all news", ctx))
     293                .append("</a>");
    316294         }
    317295         return buf.toString();
  • apps/routerconsole/java/src/net/i2p/router/web/RouterConsoleRunner.java

    ra2e3850 raddc9c5  
    2727import net.i2p.jetty.I2PLogger;
    2828import net.i2p.router.RouterContext;
     29import net.i2p.router.app.RouterApp;
     30import net.i2p.router.news.NewsManager;
    2931import net.i2p.router.update.ConsoleUpdateManager;
    30 import net.i2p.router.app.RouterApp;
    3132import net.i2p.util.Addresses;
    3233import net.i2p.util.FileUtil;
     
    707708            ConsoleUpdateManager um = new ConsoleUpdateManager(_context, _mgr, null);
    708709            um.start();
     710            NewsManager nm = new NewsManager(_context, _mgr, null);
     711            nm.startup();
    709712       
    710713            if (PluginStarter.pluginsEnabled(_context)) {
  • apps/routerconsole/java/src/net/i2p/router/web/SummaryBarRenderer.java

    ra2e3850 raddc9c5  
    44import java.io.IOException;
    55import java.io.Writer;
     6import java.text.DateFormat;
    67import java.util.Collections;
     8import java.util.Date;
    79import java.util.HashMap;
    810import java.util.List;
    911import java.util.Map;
    10 
     12import java.util.TimeZone;
     13
     14import net.i2p.app.ClientAppManager;
    1115import net.i2p.crypto.SigType;
    1216import net.i2p.data.DataHelper;
    1317import net.i2p.router.RouterContext;
     18import net.i2p.router.news.NewsEntry;
     19import net.i2p.router.news.NewsManager;
    1420import net.i2p.util.PortMapper;
    1521
     
    605611        if (consoleNonce != null) {
    606612            // Set up title and pre-headings stuff.
    607             buf.append("<h3><a href=\"/configupdate\">")
     613            //buf.append("<h3><a href=\"/configupdate\">")
     614            buf.append("<h3><a href=\"/news\">")
    608615               .append(_("News &amp; Updates"))
    609616               .append("</a></h3><hr class=\"b\"><div class=\"newsheadings\">\n");
    610617            // Get news content.
    611             String newsContent = newshelper.getContent();
    612             if (newsContent != "") {
     618            List<NewsEntry> entries = Collections.emptyList();
     619            ClientAppManager cmgr = _context.clientAppManager();
     620            if (cmgr != null) {
     621                NewsManager nmgr = (NewsManager) cmgr.getRegisteredApp(NewsManager.APP_NAME);
     622                if (nmgr != null)
     623                    entries = nmgr.getEntries();
     624            }
     625            if (!entries.isEmpty()) {
    613626                buf.append("<ul>\n");
    614                 // Parse news content for headings.
    615                 boolean foundEntry = false;
    616                 int start = newsContent.indexOf("<h3>");
    617                 while (start >= 0) {
    618                     // Add offset to start:
    619                     // 4 - gets rid of <h3>
    620                     // 16 - gets rid of the date as well (assuming form "<h3>yyyy-mm-dd: Foobarbaz...")
    621                     // Don't truncate the "congratulations" in initial news
    622                     if (newsContent.length() > start + 16 &&
    623                         newsContent.substring(start + 4, start + 6).equals("20") &&
    624                         newsContent.substring(start + 14, start + 16).equals(": "))
    625                         newsContent = newsContent.substring(start+16, newsContent.length());
    626                     else
    627                         newsContent = newsContent.substring(start+4, newsContent.length());
    628                     int end = newsContent.indexOf("</h3>");
    629                     if (end >= 0) {
    630                         String heading = newsContent.substring(0, end);
    631                         buf.append("<li><a href=\"/?news=1&amp;consoleNonce=")
    632                            .append(consoleNonce)
    633                            .append("\">")
    634                            .append(heading)
    635                            .append("</a></li>\n");
    636                         foundEntry = true;
     627                DateFormat fmt = DateFormat.getDateInstance(DateFormat.SHORT);
     628                // the router sets the JVM time zone to UTC but saves the original here so we can get it
     629                String systemTimeZone = _context.getProperty("i2p.systemTimeZone");
     630                if (systemTimeZone != null)
     631                    fmt.setTimeZone(TimeZone.getTimeZone(systemTimeZone));
     632                int i = 0;
     633                final int max = 2;
     634                for (NewsEntry entry : entries) {
     635                    buf.append("<li><a href=\"/?news=1&amp;consoleNonce=")
     636                       .append(consoleNonce)
     637                       .append("\">");
     638                    if (entry.updated > 0) {
     639                        Date date = new Date(entry.updated);
     640                        buf.append(fmt.format(date))
     641                           .append(": ");
    637642                    }
    638                     start = newsContent.indexOf("<h3>");
     643                    buf.append(entry.title)
     644                       .append("</a></li>\n");
     645                    if (++i >= max)
     646                        break;
    639647                }
    640648                buf.append("</ul>\n");
    641                 // Set up string containing <a> to show news.
    642                 String requestURI = _helper.getRequestURI();
    643                 if (requestURI.contains("/home") && !foundEntry) {
    644                     buf.append("<a href=\"/?news=1&amp;consoleNonce=")
    645                        .append(consoleNonce)
    646                        .append("\">")
    647                        .append(_("Show news"))
    648                        .append("</a>\n");
    649                 }
     649                //buf.append("<a href=\"/news\">")
     650                //   .append(_("Show all news"))
     651                //   .append("</a>\n");
    650652            } else {
    651653                buf.append("<center><i>")
  • apps/routerconsole/jsp/news.jsp

    ra2e3850 raddc9c5  
    1515<jsp:setProperty name="feedHelper" property="contextId" value="<%=(String)session.getAttribute(\"i2p.contextId\")%>" />
    1616<% feedHelper.setLimit(0); %>
     17<div class="fixme" id="fixme">
    1718<jsp:getProperty name="feedHelper" property="entries" />
    18 </div></body></html>
     19</div></div></body></html>
  • history.txt

    ra2e3850 raddc9c5  
     12015-09-15 zzz
     2 * Console:
     3   - Store news feed items separately on disk in XML, like a real feed reader
     4   - Limit display to 2 news items in summary bar, /home and /console
     5   - New /news page to show all news (ticket #1425)
     6
    17* 2015-09-12 0.9.22 released
    28
  • router/java/src/net/i2p/router/RouterVersion.java

    ra2e3850 raddc9c5  
    1919    public final static String ID = "Monotone";
    2020    public final static String VERSION = CoreVersion.VERSION;
    21     public final static long BUILD = 1;
     21    public final static long BUILD = 2;
    2222
    2323    /** for example "-test" */
Note: See TracChangeset for help on using the changeset viewer.