0001: package org.methodize.nntprss.admin;
0002:
0003: /* -----------------------------------------------------------
0004: * nntp//rss - a bridge between the RSS world and NNTP clients
0005: * Copyright (c) 2002, 2003 Jason Brome. All Rights Reserved.
0006: *
0007: * email: nntprss@methodize.org
0008: * mail: Methodize Solutions
0009: * PO Box 3865
0010: * Grand Central Station
0011: * New York NY 10163
0012: *
0013: * This file is part of nntp//rss
0014: *
0015: * nntp//rss is free software; you can redistribute it
0016: * and/or modify it under the terms of the GNU General
0017: * Public License as published by the Free Software Foundation;
0018: * either version 2 of the License, or (at your option) any
0019: * later version.
0020: *
0021: * This program is distributed in the hope that it will be
0022: * useful, but WITHOUT ANY WARRANTY; without even the implied
0023: * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
0024: * PURPOSE. See the GNU General Public License for more
0025: * details.
0026: *
0027: * You should have received a copy of the GNU General Public
0028: * License along with this program; if not, write to the
0029: * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
0030: * Boston, MA 02111-1307 USA
0031: * ----------------------------------------------------- */
0032:
0033: import java.io.IOException;
0034: import java.io.PrintWriter;
0035: import java.io.Writer;
0036: import java.net.MalformedURLException;
0037: import java.net.URL;
0038: import java.net.URLEncoder;
0039: import java.text.DateFormat;
0040: import java.util.ArrayList;
0041: import java.util.Date;
0042: import java.util.Enumeration;
0043: import java.util.HashMap;
0044: import java.util.Iterator;
0045: import java.util.List;
0046: import java.util.Map;
0047: import java.util.Stack;
0048: import java.util.StringTokenizer;
0049:
0050: import javax.servlet.ServletException;
0051: import javax.servlet.http.HttpServlet;
0052: import javax.servlet.http.HttpServletRequest;
0053: import javax.servlet.http.HttpServletResponse;
0054: import javax.xml.parsers.DocumentBuilder;
0055: import javax.xml.parsers.ParserConfigurationException;
0056:
0057: import org.methodize.nntprss.nntp.NNTPServer;
0058: import org.methodize.nntprss.rss.Channel;
0059: import org.methodize.nntprss.rss.ChannelManager;
0060: import org.methodize.nntprss.rss.publish.BloggerPublisher;
0061: import org.methodize.nntprss.rss.publish.LiveJournalPublisher;
0062: import org.methodize.nntprss.rss.publish.MetaWeblogPublisher;
0063: import org.methodize.nntprss.rss.publish.Publisher;
0064: import org.methodize.nntprss.rss.publish.PublisherException;
0065: import org.methodize.nntprss.util.AppConstants;
0066: import org.methodize.nntprss.util.HTMLHelper;
0067: import org.methodize.nntprss.util.RSSHelper;
0068: import org.methodize.nntprss.util.XMLHelper;
0069: import org.mortbay.servlet.MultiPartRequest;
0070: import org.w3c.dom.Document;
0071: import org.w3c.dom.Element;
0072: import org.w3c.dom.NodeList;
0073: import org.xml.sax.SAXException;
0074:
0075: /**
0076: * @author Jason Brome <jason@methodize.org>
0077: * @version $Id: AdminServlet.java,v 1.7 2003/03/24 03:11:15 jasonbrome Exp $
0078: *
0079: * Web Administration interface for nntp//rss
0080: *
0081: * In its current implementation, it's a rather unelegant
0082: * bundle of admin + presentation logic. When I find the
0083: * right lightweight template-driven solution, I'll switch
0084: * over to that...
0085: *
0086: */
0087: public class AdminServlet extends HttpServlet {
0088:
0089: private static final String CSS_HEADER = "<style type='text/css'>"
0090: + "<!--"
0091: // + "a:link,a:active,a:visited { color : #FFF240; } "
0092: // + "a:hover { text-decoration: underline; color : #FFF240; } "
0093: + "body { background-color: #E5E5E5; } "
0094: + ".bodyborder { background-color: #FFFFFF; border: 1px #98AAB1 solid; } "
0095: + ".tableborder { background-color: #FFFFFF; border: 2px #006699 solid; } "
0096: + ".smalltext { font-family: Verdana, Arial, Helvetica, sans-serif; font-size : 9px; } "
0097: + "font,th,td,p { font-family: Verdana, Arial, Helvetica, sans-serif; font-size : 12px; } "
0098: + "td.chlerror { background-color: #FF0000; } "
0099: + "td.chlwarning { background-color: #FFFF00; } "
0100: + "td.chldisabled { background-color: #CCCCCC; } "
0101: + "td.row1 { background-color: #EFEFEF; } "
0102: + "td.row2 { background-color: #DEE3E7; } "
0103: + "a.head { color: #FFFFFF; text-decoration: none} "
0104: + "a:hover.head { text-decoration: underline; color : #FFF240; } "
0105: + "a.row { text-decoration: none} "
0106: + "a:hover.row { text-decoration: underline } "
0107: + "th { color: #FFF240; font-size: 11px; font-weight : bold; background-color: #408BFF; height: 25px; } "
0108: + "th.subHead { background-color: #2D62B3; color: #FFFFFF; height: 18px;} "
0109: + "input,textarea, select { color : #000000; font: normal 11px Verdana, Arial, Helvetica, sans-serif; border-width: 2px; border-color : #000000; } "
0110: + "-->" + "</style>";
0111:
0112: // Magic value to indicate that the current password should not be changed.
0113: // Currently used within posting/publishing configuration.
0114: private static final String PASSWORD_MAGIC_KEY = "###__KCV__###";
0115:
0116: private void writeHeader(Writer writer) throws IOException {
0117:
0118: writer.write("<html><head><title>nntp//rss admin</title>");
0119: writer.write(CSS_HEADER);
0120: writer.write("</head>");
0121: // writer.write("<body topmargin='0' leftmargin='0' marginheight='0' marginwidth='0' bgcolor='#ffffff' link='#0000FF' alink='#0000FF' vlink='#0000FF'>\n");
0122: writer
0123: .write("<body bgcolor='#ffffff' link='#0000FF' alink='#0000FF' vlink='#0000FF'>\n");
0124:
0125: writer
0126: .write("<table width='100%' border='0' cellspacing='0' cellpadding='2'><tr><td class='bodyborder' bgcolor='#FFFFFF'>");
0127:
0128: writer
0129: .write("<table width='100%' border='0' cellspacing='3' cellpadding='0'>");
0130: writer
0131: .write("<tr><th colspan='5'>nntp//rss Administration</th></tr>");
0132: writer.write("<tr>");
0133: writer.write("<th class='subHead' width='50%'> </th>");
0134: writer
0135: .write("<th class='subHead' nowrap='nowrap'> <a class='head' href='/'>View Channels</a> </td>");
0136: writer
0137: .write("<th class='subHead' nowrap='nowrap'> <a class='head' href='?action=addform'>Add Channel</a> </td>");
0138: writer
0139: .write("<th class='subHead' nowrap='nowrap'> <a class='head' href='?action=showconfig'>System Configuration</a> </td>");
0140: writer
0141: .write("<th class='subHead' width='50%' align='right'><a class='head' href='?action=help'>Help</a> </th>");
0142: writer.write("</tr>");
0143: writer.write("</table>");
0144:
0145: writer
0146: .write("<table border='0' cellspacing='0' cellpadding='0' height='100%' width='100%'>");
0147: // writer.write("<tr><td colspan='3' width='100%' bgcolor='#dddddd'><font size='+2'><b> nntp//rss Admin</b></font><hr width='100%'></td></tr>");
0148: // writer.write("<tr height='100%'><td valign='top' bgcolor='#dddddd'><br><a href='/'>View Channels</a><p><a href='?action=addform'>Add Channel</a><p><a href='?action=showconfig'>System Configuration</a></td><td> </td><td width='90%' height='100%' valign='top'><br>");
0149: writer
0150: .write("<tr height='100%'><td width='100%' height='100%' valign='top' align='center'><br>");
0151: }
0152:
0153: private void writeFooter(Writer writer) throws IOException {
0154: DateFormat df = DateFormat.getTimeInstance(DateFormat.MEDIUM);
0155:
0156: writer.write("<p>");
0157: writer.write("</td></tr></table>");
0158:
0159: writer
0160: .write("<table cellspacing='0' cellpadding='2' width='100%'><tr><td class='row2'>nntp//rss v"
0161: + AppConstants.VERSION
0162: + "</td><td class='row2' align='center'>nntp//rss Time: "
0163: + df.format(new Date())
0164: + "</td><td class='row2' align='right'><a href='http://www.methodize.org/nntprss'>nntp//rss home page</a> </td></tr></table>");
0165:
0166: writer.write("</td></tr></table>");
0167:
0168: writer.write("</body></html>");
0169: }
0170:
0171: private void writeConfig(Writer writer,
0172: ChannelManager channelManager, NNTPServer nntpServer)
0173: throws IOException {
0174: writer
0175: .write("<form action='?action=updateconfig' method='POST'>");
0176: writer.write("<table class='tableBorder'>");
0177:
0178: writer
0179: .write("<tr><th colspan='2' class='titleHead'>System Configuration</th></tr>");
0180:
0181: writer
0182: .write("<tr><td class='row1' align='right'><nobr>Default Channel Polling Interval<nobr></td>");
0183: writer
0184: .write("<td class='row2'>Every <select name='pollingInterval'>");
0185: writer.write("<option selected value='"
0186: + channelManager.getPollingIntervalSeconds() + "'>"
0187: + channelManager.getPollingIntervalSeconds() / 60
0188: + "\n");
0189: for (int interval = 10; interval <= 120; interval += 10) {
0190: writer.write("<option value='" + (interval * 60) + "'>"
0191: + interval + "\n");
0192: }
0193: writer.write("</select> minutes </td></tr>");
0194:
0195: writer
0196: .write("<tr><td class='row1' align='right'>Proxy Server Hostname</td><td class='row2'><input type='text' name='proxyServer' value='"
0197: + (channelManager.getProxyServer() == null ? ""
0198: : channelManager.getProxyServer())
0199: + "'><br><i>Host name of your proxy server, leave blank if no proxy</i></td></tr>");
0200: writer
0201: .write("<tr><td class='row1' align='right'>Proxy Server Port</td><td class='row2'><input type='text' name='proxyPort' value='"
0202: + (channelManager.getProxyPort() == 0 ? ""
0203: : Integer.toString(channelManager
0204: .getProxyPort()))
0205: + "'><br><i>Proxy server listener port, leave blank if no proxy</i></td></tr>");
0206: writer
0207: .write("<tr><td class='row1' align='right'>Proxy User ID</td><td class='row2'><input type='text' name='proxyUserID' value='"
0208: + ((channelManager.getProxyUserID() == null) ? ""
0209: : channelManager.getProxyUserID())
0210: + "'><br><i>Proxy userid, leave blank if no userid</i></td></tr>");
0211: writer
0212: .write("<tr><td class='row1' align='right'>Proxy Password</td><td class='row2'><input type='password' name='proxyPassword' value='"
0213: + ((channelManager.getProxyPassword() == null) ? ""
0214: : channelManager.getProxyPassword())
0215: + "'><br><i>Proxy password, leave blank if no password</i></td></tr>");
0216:
0217: writer
0218: .write("<tr><td class='row1' align='right'>Content Type</td>");
0219: writer.write("<td class='row2'><select name='contentType'>");
0220: int contentType = nntpServer.getContentType();
0221: writer
0222: .write("<option value='"
0223: + AppConstants.CONTENT_TYPE_MIXED
0224: + "'"
0225: + (contentType == AppConstants.CONTENT_TYPE_MIXED ? " selected"
0226: : "")
0227: + ">Text & HTML (multipart/alternative)");
0228: writer
0229: .write("<option value='"
0230: + AppConstants.CONTENT_TYPE_TEXT
0231: + "'"
0232: + (contentType == AppConstants.CONTENT_TYPE_TEXT ? " selected"
0233: : "") + ">Text (text/plain)");
0234: writer
0235: .write("<option value='"
0236: + AppConstants.CONTENT_TYPE_HTML
0237: + "'"
0238: + (contentType == AppConstants.CONTENT_TYPE_HTML ? " selected"
0239: : "") + ">HTML (text/html)");
0240:
0241: writer.write("</select></td></tr>");
0242:
0243: writer
0244: .write("<tr><td class='row1' align='right'>Secure NNTP</td>");
0245: writer.write("<td class='row2'><select name='nntpSecure'>");
0246: boolean nntpSecure = nntpServer.isSecure();
0247: writer.write("<option value='true'"
0248: + (nntpSecure ? " selected" : "") + ">Yes");
0249: writer.write("<option value='false'"
0250: + ((!nntpSecure) ? " selected" : "") + ">No");
0251: writer.write("</select></td></tr>");
0252:
0253: writer
0254: .write("<tr><td class='row2' align='center' colspan='2'><input type='submit' name='update' value='Update'></td></tr>");
0255: writer.write("</table>");
0256: writer.write("</form>");
0257: writer.write("<p>");
0258: writer
0259: .write("<a class='row' href='/?action=export'>Export nntp//rss Channel List</a><p>");
0260: writer
0261: .write("<a class='row' href='/?action=importform'>Import nntp//rss or mySubscriptions.opml Channel List</a>");
0262: }
0263:
0264: private void cmdShowConfig(HttpServletRequest request,
0265: HttpServletResponse response) throws ServletException,
0266: IOException {
0267:
0268: Writer writer = response.getWriter();
0269: writeHeader(writer);
0270:
0271: ChannelManager channelManager = (ChannelManager) getServletContext()
0272: .getAttribute(AdminServer.SERVLET_CTX_RSS_MANAGER);
0273: NNTPServer nntpServer = (NNTPServer) getServletContext()
0274: .getAttribute(AdminServer.SERVLET_CTX_NNTP_SERVER);
0275:
0276: writeConfig(writer, channelManager, nntpServer);
0277: writeFooter(writer);
0278: }
0279:
0280: private void cmdUpdateConfig(HttpServletRequest request,
0281: HttpServletResponse response) throws ServletException,
0282: IOException {
0283:
0284: Writer writer = response.getWriter();
0285: writeHeader(writer);
0286:
0287: ChannelManager channelManager = (ChannelManager) getServletContext()
0288: .getAttribute(AdminServer.SERVLET_CTX_RSS_MANAGER);
0289: NNTPServer nntpServer = (NNTPServer) getServletContext()
0290: .getAttribute(AdminServer.SERVLET_CTX_NNTP_SERVER);
0291:
0292: nntpServer.setContentType(Integer.parseInt(request
0293: .getParameter("contentType")));
0294: nntpServer.setSecure(request.getParameter("nntpSecure")
0295: .equalsIgnoreCase("true"));
0296: nntpServer.saveConfiguration();
0297:
0298: channelManager.setPollingIntervalSeconds(Long.parseLong(request
0299: .getParameter("pollingInterval")));
0300: channelManager.setProxyServer(request.getParameter(
0301: "proxyServer").trim());
0302:
0303: String proxyPortStr = request.getParameter("proxyPort");
0304: boolean validPort = true;
0305:
0306: if (proxyPortStr.length() == 0) {
0307: channelManager.setProxyPort(0);
0308: } else {
0309: try {
0310: channelManager.setProxyPort(Integer
0311: .parseInt(proxyPortStr));
0312: } catch (NumberFormatException nfe) {
0313: validPort = false;
0314: }
0315: }
0316:
0317: channelManager.setProxyUserID(request.getParameter(
0318: "proxyUserID").trim());
0319: channelManager.setProxyPassword(request.getParameter(
0320: "proxyPassword").trim());
0321:
0322: if (validPort == true) {
0323: channelManager.saveConfiguration();
0324: writer
0325: .write("System configuration successfully updated.<p>");
0326: } else {
0327: writer
0328: .write("<b>Proxy port must either be blank or a numeric value!</b><p>");
0329: }
0330:
0331: writeConfig(writer, channelManager, nntpServer);
0332: writeFooter(writer);
0333: }
0334:
0335: private void writeChannel(Writer writer, Channel channel,
0336: HttpServletRequest request, boolean refresh)
0337: throws IOException {
0338: if (channel == null) {
0339: writer.write("<b>Channel " + channel.getName()
0340: + " not found!</b>");
0341: } else {
0342: DateFormat df = DateFormat.getDateTimeInstance(
0343: DateFormat.FULL, DateFormat.FULL);
0344:
0345: String url = ((!refresh) ? channel.getUrl() : request
0346: .getParameter("URL"));
0347: boolean enabled = ((!refresh) ? channel.isEnabled()
0348: : request.getParameter("enabled").equals("true"));
0349: boolean parseAtAllCost = ((!refresh) ? channel
0350: .isParseAtAllCost() : request.getParameter(
0351: "parseAtAllCost").equals("true"));
0352: boolean historical = ((!refresh) ? channel.isHistorical()
0353: : request.getParameter("historical").equals("true"));
0354: boolean postingEnabled = ((!refresh) ? channel
0355: .isPostingEnabled() : (!request.getParameter(
0356: "postingEnabled").equals("false")));
0357: String publishAPI = ((!refresh) ? channel.getPublishAPI()
0358: : request.getParameter("publishAPI"));
0359: long pollingIntervalSeconds = ((!refresh) ? channel
0360: .getPollingIntervalSeconds() : Long
0361: .parseLong(request.getParameter("pollingInterval")));
0362:
0363: writer
0364: .write("<form name='channel' action='?action=update' method='POST'>");
0365: writer
0366: .write("<input type='hidden' name='name' value='"
0367: + HTMLHelper
0368: .escapeString(channel.getName())
0369: + "'>");
0370: writer.write("<table class='tableborder'>");
0371:
0372: writer
0373: .write("<tr><th class='tableHead' colspan='2'>Channel Configuration</th></tr>");
0374:
0375: writer
0376: .write("<tr><td class='row1' align='right'>Name</td><td class='row2'>"
0377: + channel.getName() + "</td></tr>");
0378: writer
0379: .write("<tr><td class='row1' align='right'>URL</td><td class='row2'><input type='text' name='URL' value='"
0380: + HTMLHelper.escapeString(url)
0381: + "' size='64'></td></tr>");
0382: writer
0383: .write("<tr><td class='row1' align='right'>Polling</td><td class='row2'><select name='enabled'>"
0384: + "<option value='true' "
0385: + (enabled ? "selected" : "")
0386: + ">Enabled"
0387: + "<option value='false' "
0388: + (!enabled ? "selected" : "")
0389: + ">Disabled" + "</select></td></tr>");
0390:
0391: writer
0392: .write("<tr><td class='row1' align='right'>Polling Interval</td>");
0393: writer
0394: .write("<td class='row2'><select name='pollingInterval'>");
0395:
0396: if (channel.getPollingIntervalSeconds() == Channel.DEFAULT_POLLING_INTERVAL) {
0397: writer.write("<option selected value='"
0398: + Channel.DEFAULT_POLLING_INTERVAL
0399: + "'>Use Default Polling Interval\n");
0400: } else {
0401: writer.write("<option selected value='"
0402: + channel.getPollingIntervalSeconds() + "'>"
0403: + channel.getPollingIntervalSeconds() / 60
0404: + " minutes\n");
0405: }
0406:
0407: writer.write("<option value='"
0408: + Channel.DEFAULT_POLLING_INTERVAL
0409: + "'>Use Default Polling Interval\n");
0410: for (int interval = 10; interval <= 120; interval += 10) {
0411: writer.write("<option value='" + (interval * 60) + "'>"
0412: + interval + " minutes\n");
0413: }
0414: writer.write("</select></td></tr>");
0415:
0416: writer
0417: .write("<tr><td class='row1' align='right'>Parse-at-all-cost</td><td class='row2'><select name='parseAtAllCost'>"
0418: + "<option value='true' "
0419: + (parseAtAllCost ? "selected" : "")
0420: + ">Enabled"
0421: + "<option value='false' "
0422: + (!parseAtAllCost ? "selected" : "")
0423: + ">Disabled"
0424: + "</select>"
0425: + "<br><i>This will enable the experimental parse-at-all-cost RSS parser. This feature supports the parsing of badly-formatted RSS feeds.</i></td></tr>");
0426:
0427: writer
0428: .write("<tr><td class='row1' align='right'>Status</td>");
0429:
0430: switch (channel.getStatus()) {
0431: case Channel.STATUS_NOT_FOUND:
0432: writer
0433: .write("<td class='chlerror' bgcolor='#FF0000'><font color='#FFFFFF'>RSS web server is returning File Not Found.</font>");
0434: break;
0435: case Channel.STATUS_INVALID_CONTENT:
0436: writer
0437: .write("<td class='chlerror' bgcolor='#FF0000'><font color='#FFFFFF'>Last RSS document retrieved could not be parsed, check URL.</font>");
0438: break;
0439: case Channel.STATUS_UNKNOWN_HOST:
0440: writer
0441: .write("<td class='chlerror' bgcolor='#FF0000'><font color='#FFFFFF'>Unable to contact RSS web server (Unknown Host). Check URL.</font>");
0442: break;
0443: case Channel.STATUS_NO_ROUTE_TO_HOST:
0444: writer
0445: .write("<td class='chlerror' bgcolor='#FF0000'><font color='#FFFFFF'>Unable to contact RSS web server (No Route To Host). Check URL.</font>");
0446: break;
0447: case Channel.STATUS_CONNECTION_TIMEOUT:
0448: writer
0449: .write("<td class='chlwarning' bgcolor='#FFFF00'><font color='#000000'>Currently unable to contact RSS web server (Connection timeout).</font>");
0450: break;
0451: case Channel.STATUS_SOCKET_EXCEPTION:
0452: writer
0453: .write("<td class='chlwarning' bgcolor='#FFFF00'><font color='#000000'>Currently unable to contact RSS web server (Socket exception).</font>");
0454: break;
0455: default:
0456: writer.write("<td class='row2'>OK");
0457: }
0458:
0459: writer.write("</td></tr>");
0460:
0461: writer
0462: .write("<tr><td class='row1' align='right'>Last Polled</td><td class='row2'>"
0463: + ((channel.getLastPolled() == null) ? "Yet to be polled."
0464: : df
0465: .format(channel
0466: .getLastPolled()))
0467: + "</td></tr>");
0468: writer
0469: .write("<tr><td class='row1' align='right'>Last Modified</td><td class='row2'>");
0470: if (channel.getLastModified() == 0) {
0471: writer
0472: .write("Last modified not supplied by RSS Web Server");
0473: } else {
0474: writer.write(df.format(new Date(channel
0475: .getLastModified())));
0476: }
0477: writer.write("</td></tr>");
0478: writer
0479: .write("<tr><td class='row1' align='right'>Last ETag</td><td class='row2'>");
0480: if (channel.getLastETag() == null) {
0481: writer.write("ETag not supplied by RSS Web Server");
0482: } else {
0483: writer.write(channel.getLastETag());
0484: }
0485: writer.write("</td></tr>");
0486: writer
0487: .write("<tr><td class='row1' align='right'>RSS Version</td><td class='row2'>");
0488: if (channel.getRssVersion() == null) {
0489: writer.write("Unknown");
0490: } else {
0491: writer.write(channel.getRssVersion());
0492: }
0493: writer.write("</td></tr>");
0494: writer
0495: .write("<tr><td class='row1' align='right'>Historical</td><td class='row2'><select name='historical'>"
0496: + "<option "
0497: + (historical ? "selected" : "")
0498: + ">true"
0499: + "<option "
0500: + (!historical ? "selected" : "")
0501: + ">false" + "</select></td></tr>");
0502: writer
0503: .write("<tr><td class='row1' align='right'>Managing Editor</td><td class='row2'>");
0504: if (channel.getManagingEditor() != null) {
0505: writer.write("<a href='mailto:"
0506: + URLEncoder
0507: .encode(RSSHelper.parseEmail(channel
0508: .getManagingEditor()))
0509: + "'>"
0510: + HTMLHelper.escapeString(channel
0511: .getManagingEditor()) + "</a>");
0512: } else {
0513: writer.write("Unknown");
0514: }
0515:
0516: writer.write("</td></tr>");
0517: writer
0518: .write("<tr><td class='row1' align='right'>Posting</td><td class='row2'><select name='postingEnabled' onChange='this.form.action=\"?action=editchlrefresh\"; this.form.submit();'>"
0519: + "<option "
0520: + (postingEnabled ? "selected" : "")
0521: + ">true"
0522: + "<option "
0523: + (!postingEnabled ? "selected" : "")
0524: + ">false" + "</select></td></tr>");
0525:
0526: if (postingEnabled) {
0527:
0528: writer
0529: .write("<tr><th class='subHead' colspan='2' align='center'>Posting Configuration</td></tr>");
0530:
0531: writer
0532: .write("<tr><td class='row1' align='right'>API</td><td class='row2'><select name='publishAPI' onChange='this.form.action=\"?action=editchlrefresh&publishapichange=true\"; this.form.submit();'>"
0533: + "<option value='blogger' "
0534: + (publishAPI == null
0535: || publishAPI.equals("blogger") ? "selected"
0536: : "")
0537: + ">Blogger"
0538: + "<option value='livejournal' "
0539: + (publishAPI != null
0540: && publishAPI
0541: .equals("livejournal") ? "selected"
0542: : "")
0543: + ">LiveJournal"
0544: + "<option value='metaweblog' "
0545: + (publishAPI != null
0546: && publishAPI
0547: .equals("metaweblog") ? "selected"
0548: : "") + ">MetaWeblog"
0549: // + "<option " + (!postingEnabled ? "selected" : "") + ">false"
0550: + "</select></td></tr>");
0551:
0552: if (publishAPI == null || publishAPI.equals("blogger")) {
0553: // Default API
0554: String publishUrl = null;
0555: String blogId = null;
0556: String userName = null;
0557: String password = null;
0558: boolean autoPublish = true;
0559:
0560: if (refresh) {
0561: // If a refresh, get the parameter values from the parameter collection
0562: if (request.getParameter("publishapichange") == null) {
0563: publishUrl = request
0564: .getParameter(BloggerPublisher.PROP_PUBLISHER_URL);
0565: }
0566: blogId = (String) request
0567: .getParameter(BloggerPublisher.PROP_BLOG_ID);
0568: userName = (String) request
0569: .getParameter(BloggerPublisher.PROP_USERNAME);
0570: password = (String) request
0571: .getParameter(BloggerPublisher.PROP_PASSWORD);
0572: String autoPublishStr = request
0573: .getParameter(BloggerPublisher.PROP_PUBLISH);
0574: autoPublish = (autoPublishStr != null && autoPublishStr
0575: .equals("false")) ? false : true;
0576: } else {
0577: // If a initial channel view, extract parameter from the Channel publish config map
0578: Map publishConfig = channel.getPublishConfig();
0579: if (publishConfig != null) {
0580: publishUrl = (String) publishConfig
0581: .get(BloggerPublisher.PROP_PUBLISHER_URL);
0582: blogId = (String) publishConfig
0583: .get(BloggerPublisher.PROP_BLOG_ID);
0584: userName = (String) publishConfig
0585: .get(BloggerPublisher.PROP_USERNAME);
0586: password = (String) publishConfig
0587: .get(BloggerPublisher.PROP_PASSWORD);
0588: if (password != null)
0589: password = PASSWORD_MAGIC_KEY;
0590: String autoPublishStr = (String) publishConfig
0591: .get(BloggerPublisher.PROP_PUBLISH);
0592: autoPublish = (autoPublishStr != null && autoPublishStr
0593: .equals("false")) ? false : true;
0594: }
0595: }
0596:
0597: // Make sure that everything has a value, especially if publish has just been enabled
0598: if (publishUrl == null)
0599: publishUrl = "http://plant.blogger.com/api/RPC2";
0600: if (blogId == null)
0601: blogId = "";
0602: if (userName == null)
0603: userName = "";
0604: if (password == null)
0605: password = "";
0606:
0607: writer
0608: .write("<tr><td class='row1' align='right'>URL</td>");
0609: writer.write("<td class='row2'><input name='"
0610: + BloggerPublisher.PROP_PUBLISHER_URL
0611: + "' type='text' size='64' value='"
0612: + HTMLHelper.escapeString(publishUrl)
0613: + "'></td></tr>");
0614:
0615: writer
0616: .write("<tr><td class='row1' align='right'>Blog Id</td>");
0617: writer.write("<td class='row2'><input name='"
0618: + BloggerPublisher.PROP_BLOG_ID
0619: + "' type='text' value='"
0620: + HTMLHelper.escapeString(blogId)
0621: + "'></td></tr>");
0622:
0623: writer
0624: .write("<tr><td class='row1' align='right'>Username</td>");
0625: writer.write("<td class='row2'><input name='"
0626: + BloggerPublisher.PROP_USERNAME
0627: + "' type='text' value='"
0628: + HTMLHelper.escapeString(userName)
0629: + "'></td></tr>");
0630:
0631: writer
0632: .write("<tr><td class='row1' align='right'>Password</td>");
0633: writer.write("<td class='row2'><input name='"
0634: + BloggerPublisher.PROP_PASSWORD
0635: + "' type='password' value='"
0636: + HTMLHelper.escapeString(password)
0637: + "'></td></tr>");
0638:
0639: writer
0640: .write("<tr><td class='row1' align='right'>Auto Publish</td>");
0641: writer.write("<td class='row2'><input name='"
0642: + BloggerPublisher.PROP_PUBLISH
0643: + "' type='checkbox' value='true' "
0644: + (autoPublish ? "checked" : "")
0645: + "></td></tr>");
0646:
0647: } else if (publishAPI.equals("metaweblog")) {
0648: String publishUrl = null;
0649: String blogId = null;
0650: String userName = null;
0651: String password = null;
0652: boolean autoPublish = true;
0653:
0654: if (refresh) {
0655: // If a refresh, get the parameter values from the parameter collection
0656: if (request.getParameter("publishapichange") == null) {
0657: publishUrl = request
0658: .getParameter(MetaWeblogPublisher.PROP_PUBLISHER_URL);
0659: }
0660: blogId = (String) request
0661: .getParameter(BloggerPublisher.PROP_BLOG_ID);
0662: if (blogId != null && blogId.length() == 0)
0663: blogId = "home";
0664:
0665: userName = (String) request
0666: .getParameter(MetaWeblogPublisher.PROP_USERNAME);
0667: password = (String) request
0668: .getParameter(MetaWeblogPublisher.PROP_PASSWORD);
0669: String autoPublishStr = request
0670: .getParameter(MetaWeblogPublisher.PROP_PUBLISH);
0671: autoPublish = (autoPublishStr != null && autoPublishStr
0672: .equals("false")) ? false : true;
0673: } else {
0674: // If a initial channel view, extract parameter from the Channel publish config map
0675: Map publishConfig = channel.getPublishConfig();
0676: if (publishConfig != null) {
0677: publishUrl = (String) publishConfig
0678: .get(MetaWeblogPublisher.PROP_PUBLISHER_URL);
0679: userName = (String) publishConfig
0680: .get(MetaWeblogPublisher.PROP_USERNAME);
0681: password = (String) publishConfig
0682: .get(MetaWeblogPublisher.PROP_PASSWORD);
0683: if (password != null)
0684: password = PASSWORD_MAGIC_KEY;
0685: String autoPublishStr = (String) publishConfig
0686: .get(MetaWeblogPublisher.PROP_PUBLISH);
0687: autoPublish = (autoPublishStr != null && autoPublishStr
0688: .equals("false")) ? false : true;
0689: }
0690: }
0691:
0692: // Make sure that everything has a value, especially if publish has just been enabled
0693: if (publishUrl == null)
0694: publishUrl = "http://127.0.0.1:5335/RPC2";
0695: if (blogId == null)
0696: blogId = "home";
0697: if (userName == null)
0698: userName = "";
0699: if (password == null)
0700: password = "";
0701:
0702: writer
0703: .write("<tr><td class='row1' align='right'>URL</td>");
0704: writer
0705: .write("<td class='row2'><input name='"
0706: + MetaWeblogPublisher.PROP_PUBLISHER_URL
0707: + "' type='text' size='64' value='"
0708: + HTMLHelper
0709: .escapeString(publishUrl)
0710: + "'><br><i>Ensure that the URL points to your MetaWeblog (e.g. Radio Userland) host</i></td></tr>");
0711:
0712: writer
0713: .write("<tr><td class='row1' align='right'>Blog Id</td>");
0714: writer.write("<td class='row2'><input name='"
0715: + BloggerPublisher.PROP_BLOG_ID
0716: + "' type='text' value='"
0717: + HTMLHelper.escapeString(blogId)
0718: + "'></td></tr>");
0719:
0720: writer
0721: .write("<tr><td class='row1' align='right'>Username</td>");
0722: writer.write("<td class='row2'><input name='"
0723: + MetaWeblogPublisher.PROP_USERNAME
0724: + "' type='text' value='"
0725: + HTMLHelper.escapeString(userName)
0726: + "'></td></tr>");
0727:
0728: writer
0729: .write("<tr><td class='row1' align='right'>Password</td>");
0730: writer.write("<td class='row2'><input name='"
0731: + MetaWeblogPublisher.PROP_PASSWORD
0732: + "' type='password' value='"
0733: + HTMLHelper.escapeString(password)
0734: + "'></td></tr>");
0735:
0736: writer
0737: .write("<tr><td class='row1' align='right'>Auto Publish</td>");
0738: writer.write("<td class='row2'><input name='"
0739: + MetaWeblogPublisher.PROP_PUBLISH
0740: + "' type='checkbox' value='true' "
0741: + (autoPublish ? "checked" : "")
0742: + "></td></tr>");
0743:
0744: } else if (publishAPI.equals("livejournal")) {
0745: String publishUrl = null;
0746: String userName = null;
0747: String password = null;
0748:
0749: if (refresh) {
0750: // If a refresh, get the parameter values from the parameter collection
0751: if (request.getParameter("publishapichange") == null) {
0752: publishUrl = request
0753: .getParameter(LiveJournalPublisher.PROP_PUBLISHER_URL);
0754: }
0755: userName = (String) request
0756: .getParameter(LiveJournalPublisher.PROP_USERNAME);
0757: password = (String) request
0758: .getParameter(LiveJournalPublisher.PROP_PASSWORD);
0759: } else {
0760: // If a initial channel view, extract parameter from the Channel publish config map
0761: Map publishConfig = channel.getPublishConfig();
0762: if (publishConfig != null) {
0763: publishUrl = (String) publishConfig
0764: .get(LiveJournalPublisher.PROP_PUBLISHER_URL);
0765: userName = (String) publishConfig
0766: .get(LiveJournalPublisher.PROP_USERNAME);
0767: password = (String) publishConfig
0768: .get(LiveJournalPublisher.PROP_PASSWORD);
0769: if (password != null)
0770: password = PASSWORD_MAGIC_KEY;
0771: }
0772: }
0773:
0774: // Make sure that everything has a value, especially if publish has just been enabled
0775: if (publishUrl == null)
0776: publishUrl = "http://www.livejournal.com/interface/xmlrpc";
0777: if (userName == null)
0778: userName = "";
0779: if (password == null)
0780: password = "";
0781:
0782: writer
0783: .write("<tr><td class='row1' align='right'>URL</td>");
0784: writer.write("<td class='row2'><input name='"
0785: + LiveJournalPublisher.PROP_PUBLISHER_URL
0786: + "' type='text' size='64' value='"
0787: + HTMLHelper.escapeString(publishUrl)
0788: + "'><br></td></tr>");
0789:
0790: writer
0791: .write("<tr><td class='row1' align='right'>Username</td>");
0792: writer.write("<td class='row2'><input name='"
0793: + LiveJournalPublisher.PROP_USERNAME
0794: + "' type='text' value='"
0795: + HTMLHelper.escapeString(userName)
0796: + "'></td></tr>");
0797:
0798: writer
0799: .write("<tr><td class='row1' align='right'>Password</td>");
0800: writer.write("<td class='row2'><input name='"
0801: + LiveJournalPublisher.PROP_PASSWORD
0802: + "' type='password' value='"
0803: + HTMLHelper.escapeString(password)
0804: + "'></td></tr>");
0805:
0806: }
0807: }
0808:
0809: writer
0810: .write("<tr><td class='row2' align='center' colspan='2'><input type='submit' name='update' value='Update'> <input type='submit' name='delete' onClick='return confirm(\"Are you sure you want to delete this channel?\");' value='Delete'></td></tr>");
0811: writer.write("</table>");
0812: writer.write("</form>");
0813: }
0814: }
0815:
0816: private void cmdShowChannel(HttpServletRequest request,
0817: HttpServletResponse response) throws ServletException,
0818: IOException {
0819:
0820: Writer writer = response.getWriter();
0821: writeHeader(writer);
0822:
0823: String channelName = request.getParameter("name");
0824:
0825: ChannelManager channelManager = (ChannelManager) getServletContext()
0826: .getAttribute(AdminServer.SERVLET_CTX_RSS_MANAGER);
0827: Channel channel = channelManager.channelByName(channelName);
0828: writeChannel(writer, channel, request, false);
0829:
0830: writeFooter(writer);
0831: }
0832:
0833: private void cmdUpdateChannel(HttpServletRequest request,
0834: HttpServletResponse response) throws ServletException,
0835: IOException {
0836:
0837: Writer writer = response.getWriter();
0838: writeHeader(writer);
0839:
0840: String channelName = request.getParameter("name");
0841: ChannelManager channelManager = (ChannelManager) getServletContext()
0842: .getAttribute(AdminServer.SERVLET_CTX_RSS_MANAGER);
0843: Channel channel = channelManager.channelByName(channelName);
0844:
0845: if (request.getParameter("update") != null) {
0846: // Update channel.
0847: String urlString = request.getParameter("URL");
0848: List errors = new ArrayList();
0849: if (urlString.length() == 0) {
0850: errors.add("URL cannot be empty");
0851: } else if (urlString.equals("http://")) {
0852: errors.add("You must specify a URL");
0853: } else if (!urlString.startsWith("http://")) {
0854: errors.add("Only URLs starting http:// are supported");
0855: }
0856:
0857: boolean postingEnabled = request.getParameter(
0858: "postingEnabled").equalsIgnoreCase("true");
0859: String publishAPI = null;
0860: Map publishConfig = null;
0861: if (postingEnabled) {
0862: publishConfig = new HashMap();
0863: publishAPI = request.getParameter("publishAPI");
0864:
0865: // Validate...
0866: //TODO: improve componentization / pluggability of publishers
0867: if (publishAPI.equals("blogger")) {
0868: publishConfig
0869: .put(
0870: BloggerPublisher.PROP_PUBLISHER_URL,
0871: request
0872: .getParameter(BloggerPublisher.PROP_PUBLISHER_URL));
0873: publishConfig
0874: .put(
0875: BloggerPublisher.PROP_USERNAME,
0876: request
0877: .getParameter(BloggerPublisher.PROP_USERNAME));
0878:
0879: String password = request
0880: .getParameter(BloggerPublisher.PROP_PASSWORD);
0881: if (password.equals(PASSWORD_MAGIC_KEY)
0882: && (channel.getPublishConfig() != null)) {
0883: password = (String) channel.getPublishConfig()
0884: .get(BloggerPublisher.PROP_PASSWORD);
0885: }
0886: publishConfig.put(BloggerPublisher.PROP_PASSWORD,
0887: password);
0888:
0889: publishConfig
0890: .put(
0891: BloggerPublisher.PROP_BLOG_ID,
0892: request
0893: .getParameter(BloggerPublisher.PROP_BLOG_ID));
0894:
0895: String autoPublishStr = (String) request
0896: .getParameter(BloggerPublisher.PROP_PUBLISH);
0897: if (autoPublishStr.equals("true")) {
0898: publishConfig.put(
0899: BloggerPublisher.PROP_PUBLISH, "true");
0900: } else {
0901: publishConfig.put(
0902: BloggerPublisher.PROP_PUBLISH, "false");
0903: }
0904:
0905: try {
0906: Publisher pub = new BloggerPublisher();
0907: pub.validate(publishConfig);
0908: } catch (PublisherException pe) {
0909: errors
0910: .add("Error validating Blogger posting configuration - "
0911: + pe.getMessage());
0912: errors
0913: .add("Check Blogger URL, user name, password, and blog id.");
0914: }
0915:
0916: } else if (publishAPI.equals("metaweblog")) {
0917: publishConfig
0918: .put(
0919: MetaWeblogPublisher.PROP_PUBLISHER_URL,
0920: request
0921: .getParameter(MetaWeblogPublisher.PROP_PUBLISHER_URL));
0922: publishConfig
0923: .put(
0924: MetaWeblogPublisher.PROP_USERNAME,
0925: request
0926: .getParameter(MetaWeblogPublisher.PROP_USERNAME));
0927:
0928: String password = request
0929: .getParameter(MetaWeblogPublisher.PROP_PASSWORD);
0930: if (password.equals(PASSWORD_MAGIC_KEY)
0931: && (channel.getPublishConfig() != null)) {
0932: password = (String) channel.getPublishConfig()
0933: .get(MetaWeblogPublisher.PROP_PASSWORD);
0934: }
0935: publishConfig
0936: .put(MetaWeblogPublisher.PROP_PASSWORD,
0937: password);
0938:
0939: publishConfig
0940: .put(
0941: MetaWeblogPublisher.PROP_BLOG_ID,
0942: request
0943: .getParameter(MetaWeblogPublisher.PROP_BLOG_ID));
0944:
0945: String autoPublishStr = (String) request
0946: .getParameter(MetaWeblogPublisher.PROP_PUBLISH);
0947: if (autoPublishStr.equals("true")) {
0948: publishConfig.put(
0949: MetaWeblogPublisher.PROP_PUBLISH,
0950: "true");
0951: } else {
0952: publishConfig.put(
0953: MetaWeblogPublisher.PROP_PUBLISH,
0954: "false");
0955: }
0956:
0957: try {
0958: Publisher pub = new MetaWeblogPublisher();
0959: pub.validate(publishConfig);
0960: } catch (PublisherException pe) {
0961: errors
0962: .add("Error validating MetaWeblog posting configuration - "
0963: + pe.getMessage());
0964: errors
0965: .add("Check URL, user name, and password.");
0966: }
0967:
0968: } else if (publishAPI.equals("livejournal")) {
0969: publishConfig
0970: .put(
0971: LiveJournalPublisher.PROP_PUBLISHER_URL,
0972: request
0973: .getParameter(LiveJournalPublisher.PROP_PUBLISHER_URL));
0974: publishConfig
0975: .put(
0976: LiveJournalPublisher.PROP_USERNAME,
0977: request
0978: .getParameter(LiveJournalPublisher.PROP_USERNAME));
0979:
0980: String password = request
0981: .getParameter(LiveJournalPublisher.PROP_PASSWORD);
0982: if (password.equals(PASSWORD_MAGIC_KEY)
0983: && (channel.getPublishConfig() != null)) {
0984: password = (String) channel
0985: .getPublishConfig()
0986: .get(LiveJournalPublisher.PROP_PASSWORD);
0987: }
0988: publishConfig.put(
0989: LiveJournalPublisher.PROP_PASSWORD,
0990: password);
0991:
0992: try {
0993: Publisher pub = new LiveJournalPublisher();
0994: pub.validate(publishConfig);
0995: } catch (PublisherException pe) {
0996: errors
0997: .add("Error validating LiveJournal posting configuration - "
0998: + pe.getMessage());
0999: errors
1000: .add("Check LiveJournal URL, user name, and password.");
1001: }
1002: }
1003:
1004: }
1005:
1006: if (errors.size() == 0) {
1007: try {
1008: boolean parseAtAllCost = request.getParameter(
1009: "parseAtAllCost").equalsIgnoreCase("true");
1010: boolean enabled = request.getParameter("enabled")
1011: .equalsIgnoreCase("true");
1012:
1013: URL url = new URL(urlString);
1014: if ((!parseAtAllCost)
1015: && (enabled && !Channel.isValid(url))) {
1016: errors
1017: .add("URL does not point to valid RSS document");
1018: errors
1019: .add("<a target='validate' href='http://feeds.archive.org/validator/check?url="
1020: + urlString
1021: + "'>Check the URL with the RSS Validator @ archive.org</a><br>");
1022: } else {
1023: channel.setUrl(url);
1024: channel.setHistorical(request.getParameter(
1025: "historical").equalsIgnoreCase("true"));
1026:
1027: channel.setEnabled(enabled);
1028: channel.setParseAtAllCost(parseAtAllCost);
1029:
1030: channel.setPostingEnabled(postingEnabled);
1031: channel.setPublishAPI(publishAPI);
1032: channel.setPublishConfig(publishConfig);
1033:
1034: channel
1035: .setPollingIntervalSeconds(Long
1036: .parseLong(request
1037: .getParameter("pollingInterval")));
1038:
1039: channel.save();
1040:
1041: if (enabled) {
1042: // Reset status and last polled date - channel should
1043: // get repolled on next iteration
1044: channel.setStatus(Channel.STATUS_OK);
1045: channel.setLastPolled(null);
1046: }
1047: }
1048: } catch (MalformedURLException me) {
1049: errors.add("URL is malformed");
1050: }
1051: }
1052:
1053: if (errors.size() == 0) {
1054: writer.write("Channel <b>" + channel.getName()
1055: + "</b> successfully updated.<p>");
1056: writeChannel(writer, channel, request, false);
1057: } else {
1058: writer
1059: .write("<b>There were errors updating the channel:</b><p>");
1060: writeErrors(writer, errors);
1061: writeChannel(writer, channel, request, true);
1062: }
1063:
1064: } else if (request.getParameter("delete") != null) {
1065: channelManager.deleteChannel(channel);
1066: writer.write("Channel <b>" + channel.getName()
1067: + "</b> successfully deleted.");
1068: }
1069:
1070: writeFooter(writer);
1071: }
1072:
1073: private void cmdEditChannelRefresh(HttpServletRequest request,
1074: HttpServletResponse response) throws ServletException,
1075: IOException {
1076:
1077: Writer writer = response.getWriter();
1078: writeHeader(writer);
1079:
1080: String channelName = request.getParameter("name");
1081: ChannelManager channelManager = (ChannelManager) getServletContext()
1082: .getAttribute(AdminServer.SERVLET_CTX_RSS_MANAGER);
1083: Channel channel = channelManager.channelByName(channelName);
1084:
1085: writeChannel(writer, channel, request, true);
1086:
1087: writeFooter(writer);
1088: }
1089:
1090: private void cmdChannelAction(HttpServletRequest request,
1091: HttpServletResponse response) throws ServletException,
1092: IOException {
1093:
1094: // Delete channels currently the only multi-channel action supported
1095: Enumeration paramEnum = request.getParameterNames();
1096: ChannelManager channelManager = (ChannelManager) getServletContext()
1097: .getAttribute(AdminServer.SERVLET_CTX_RSS_MANAGER);
1098:
1099: while (paramEnum.hasMoreElements()) {
1100: String channelName = (String) paramEnum.nextElement();
1101: if (channelName.startsWith("chl")) {
1102: // Channel to delete...
1103: channelName = channelName.substring(3);
1104: Channel channel = channelManager
1105: .channelByName(channelName);
1106: if (channel != null) {
1107: channelManager.deleteChannel(channel);
1108: }
1109: }
1110: }
1111:
1112: cmdShowCurrentChannels(request, response);
1113: }
1114:
1115: private void cmdShowCurrentChannels(HttpServletRequest request,
1116: HttpServletResponse response) throws ServletException,
1117: IOException {
1118:
1119: DateFormat df = DateFormat.getDateTimeInstance(
1120: DateFormat.MEDIUM, DateFormat.SHORT);
1121:
1122: Writer writer = response.getWriter();
1123: writeHeader(writer);
1124:
1125: writeCheckboxSelector(writer, "checkAllChannels", "chl",
1126: "channels");
1127:
1128: writer
1129: .write("<form name='channels' action='/?action=channelaction' method='POST'>");
1130: writer.write("<table class='tableborder' border='0'>");
1131: writer
1132: .write("<tr><th colspan='5' class='tableHead'>Channels</td></th>");
1133: // writer.write("<tr><th class='subHead'><input type='checkbox' name='change' onClick='checkAllChannels(this);'></th><th class='subHead'>Newsgroup Name</th><th class='subHead'>RSS URL</th><th class='subHead'>Last Polled</th></tr>");
1134: writer
1135: .write("<tr><th class='subHead'><input type='checkbox' name='change' onClick='checkAllChannels(this);'></th><th class='subHead'>Newsgroup Name</th><th class='subHead'> </th><th class='subHead'>RSS URL</th><th class='subHead'>Last Polled</th></tr>");
1136:
1137: ChannelManager channelManager = (ChannelManager) getServletContext()
1138: .getAttribute(AdminServer.SERVLET_CTX_RSS_MANAGER);
1139: NNTPServer nntpServer = (NNTPServer) getServletContext()
1140: .getAttribute(AdminServer.SERVLET_CTX_NNTP_SERVER);
1141:
1142: Iterator channelIter = channelManager.channels();
1143: String newsPrefix = null;
1144: if (nntpServer.getListenerPort() == 119) {
1145: newsPrefix = "news://127.0.0.1/";
1146: } else {
1147: newsPrefix = "news://127.0.0.1:"
1148: + nntpServer.getListenerPort() + "/";
1149: }
1150:
1151: while (channelIter.hasNext()) {
1152: Channel channel = (Channel) channelIter.next();
1153: writer
1154: .write("<tr><td class='row1'><input type='checkbox' name='chl"
1155: + HTMLHelper
1156: .escapeString(channel.getName())
1157: + "'></td>");
1158:
1159: // Truncate displayed URL...
1160: String url = channel.getUrl();
1161: if (url.length() > 32) {
1162: url = url.substring(0, 32) + "...";
1163: }
1164:
1165: String lastPolled;
1166: if (channel.getLastPolled() != null) {
1167: lastPolled = df.format(channel.getLastPolled());
1168: } else {
1169: lastPolled = "Yet to be polled";
1170: }
1171:
1172: String parser = (channel.isParseAtAllCost() ? "*" : "");
1173:
1174: switch (channel.getStatus()) {
1175: case Channel.STATUS_INVALID_CONTENT:
1176: case Channel.STATUS_NOT_FOUND:
1177: case Channel.STATUS_UNKNOWN_HOST:
1178: case Channel.STATUS_NO_ROUTE_TO_HOST:
1179: writer
1180: .write("<td class='chlerror' bgcolor='#FF0000'>"
1181: + parser
1182: + "<a class='row' title='Channel configuration' href='/?action=show&name="
1183: + URLEncoder.encode(channel.getName())
1184: + "'><font color='#FFFFFF'>"
1185: + channel.getName()
1186: + "</font></a></td>");
1187: writer
1188: .write("<td class='chlerror' bgcolor='#FF0000'><a class='row' title='Read this channel in your default newsreader' href='"
1189: + newsPrefix
1190: + HTMLHelper.escapeString(channel
1191: .getName())
1192: + "'><font color='#FFFFFF'>[Read]</font></a></td>");
1193: writer
1194: .write("<td class='chlerror' bgcolor='#FF0000'><font color='#FFFFFF'>"
1195: + url + "</font></td>");
1196: writer
1197: .write("<td class='chlerror' bgcolor='#FF0000'><font color='#FFFFFF'>"
1198: + lastPolled + "</font></td></tr>");
1199: break;
1200: case Channel.STATUS_SOCKET_EXCEPTION:
1201: case Channel.STATUS_CONNECTION_TIMEOUT:
1202: writer
1203: .write("<td class='chlwarning' bgcolor='#FFFF00'>"
1204: + parser
1205: + "<a class='row' title='Channel configuration' href='/?action=show&name="
1206: + URLEncoder.encode(channel.getName())
1207: + "'><font color='#000000'>"
1208: + channel.getName()
1209: + "</font></a></td>");
1210: writer
1211: .write("<td class='chlwarning' bgcolor='#FFFF00'><a class='row' title='Read this channel in your default newsreader' href='"
1212: + newsPrefix
1213: + HTMLHelper.escapeString(channel
1214: .getName())
1215: + "'>[Read]</a></td>");
1216: writer
1217: .write("<td class='chlwarning' bgcolor='#FFFF00'><font color='#000000'>"
1218: + url + "</font></td>");
1219: writer
1220: .write("<td class='chlwarning' bgcolor='#FFFF00'><font color='#000000'>"
1221: + lastPolled + "</font></td></tr>");
1222: break;
1223: default:
1224: if (channel.isEnabled()) {
1225: writer
1226: .write("<td class='row1'>"
1227: + parser
1228: + "<a class='row' title='Channel configuration' href='/?action=show&name="
1229: + URLEncoder.encode(channel
1230: .getName()) + "'>"
1231: + channel.getName() + "</a></td>");
1232: writer
1233: .write("<td class='row1'><a class='row' title='Read this channel in your default newsreader' href='"
1234: + newsPrefix
1235: + HTMLHelper.escapeString(channel
1236: .getName())
1237: + "'>[Read]</a></td>");
1238:
1239: writer.write("<td class='row1'>" + url + "</td>");
1240: writer.write("<td class='row1'>" + lastPolled
1241: + "</td></tr>");
1242: } else {
1243: writer
1244: .write("<td class='chldisabled' bgcolor='#CCCCCC'>"
1245: + parser
1246: + "<a class='row' title='Channel configuration' href='/?action=show&name="
1247: + URLEncoder.encode(channel
1248: .getName())
1249: + "'>"
1250: + channel.getName() + "</a></td>");
1251: writer
1252: .write("<td class='chldisabled'><a class='row' title='Read this channel in your default newsreader' href='"
1253: + newsPrefix
1254: + HTMLHelper.escapeString(channel
1255: .getName())
1256: + "'>[Read]</a></td>");
1257: writer
1258: .write("<td class='chldisabled' bgcolor='#CCCCCC'>"
1259: + url + "</td>");
1260: writer
1261: .write("<td class='chldisabled' bgcolor='#CCCCCC'>"
1262: + lastPolled + "</td></tr>");
1263: }
1264: }
1265: }
1266:
1267: writer
1268: .write("<tr><td class='row2' colspan='5'><input type='submit' onClick='return confirm(\"Are you sure you want to delete these channels?\");' name='delete' value='Delete Selected Channels'></td></tr>");
1269:
1270: writer
1271: .write("</table><font size='-1'>[* = Channel configured for Parse-At-All-Cost parser]</font><p>");
1272: writer.write("</form><p>");
1273: writeFooter(writer);
1274: writer.flush();
1275:
1276: }
1277:
1278: private void writeCheckboxSelector(Writer writer,
1279: String functionName, String checkPrefix, String formName)
1280: throws IOException {
1281: writer.write("\n<SCRIPT language='JavaScript'><!--\n");
1282:
1283: writer.write("function " + functionName + "(checkBox)\n");
1284: writer.write("{\n");
1285: writer.write(" var form = document." + formName + ";\n");
1286: writer
1287: .write(" for(var itemCount = 0; itemCount<form.elements.length; itemCount++) {\n");
1288: writer.write(" var item = form.elements[itemCount];\n");
1289: writer
1290: .write(" if(item.type == 'checkbox' && item.name.indexOf(\""
1291: + checkPrefix + "\") == 0) {\n");
1292: writer.write(" if(checkBox.checked) {\n");
1293: writer.write(" item.checked = true;\n");
1294: writer.write(" } else {\n");
1295: writer.write(" item.checked = false;\n");
1296: writer.write(" }\n");
1297: writer.write(" }\n");
1298: writer.write(" }\n");
1299: writer.write("}\n");
1300: writer.write("--></SCRIPT>\n");
1301: }
1302:
1303: private void cmdAddChannelForm(HttpServletRequest request,
1304: HttpServletResponse response) throws ServletException,
1305: IOException {
1306:
1307: // If add has been called from an external page, passing in the URL
1308: // Check for it
1309: String urlString = request.getParameter("URL");
1310: String name = request.getParameter("name");
1311: if (name != null) {
1312: name = name.trim();
1313: }
1314:
1315: if ((name == null || name.length() == 0) && urlString != null) {
1316: name = createChannelName(urlString);
1317: }
1318:
1319: String historicalStr = request.getParameter("historical");
1320: boolean historical = true;
1321: if (historicalStr != null) {
1322: historical = historicalStr.equalsIgnoreCase("true");
1323: }
1324:
1325: String validateStr = request.getParameter("validate");
1326: boolean validate = true;
1327: if (validateStr != null) {
1328: validate = validateStr.equalsIgnoreCase("true");
1329: }
1330:
1331: Writer writer = response.getWriter();
1332: writeHeader(writer);
1333: writer.write("<form action='/?action=add' method='post'>");
1334: writer.write("<table class='tableborder'>");
1335:
1336: writer.write("<tr><th colspan='2'>Add RSS Channel</th></tr>");
1337:
1338: if (name != null) {
1339: writer
1340: .write("<tr><td class='row1' align='right'>Newsgroup Name:</td><td class='row2'><input type='text' name='name' size='64' value='"
1341: + HTMLHelper.escapeString(name)
1342: + "'></td></tr>");
1343: } else {
1344: writer
1345: .write("<tr><td class='row1' align='right'>Newsgroup Name:</td><td class='row2'><input type='text' name='name' size='64'></td></tr>");
1346: }
1347:
1348: if (urlString != null && urlString.length() > 0) {
1349: writer
1350: .write("<tr><td class='row1' align='right'>RSS URL:</td><td class='row2' ><input type='text' name='url' size='64' value='"
1351: + HTMLHelper.escapeString(urlString)
1352: + "'></td></tr>");
1353: } else {
1354: writer
1355: .write("<tr><td class='row1' align='right'>RSS URL:</td><td class='row2' ><input type='text' name='url' size='64' value='http://'></td></tr>");
1356: }
1357:
1358: writer
1359: .write("<tr><td class='row1' align='right' valign='top'>Historical</td><td class='row2'><select name='historical'>"
1360: + "<option "
1361: + (historical ? "selected" : "")
1362: + ">true"
1363: + "<option "
1364: + (!historical ? "selected" : "")
1365: + ">false"
1366: + "</select><br><i>(True = Keep items removed from the original RSS document)</i></td></tr>");
1367:
1368: writer
1369: .write("<tr><td class='row1' align='right' valign='top'>Validate</td><td class='row2'><input type='checkbox' name='validate' "
1370: + (validate ? "checked" : "")
1371: + ">"
1372: + "<br><i>(Checked = Ensure URL points to a valid RSS document)</i></td></tr>");
1373:
1374: writer
1375: .write("<tr><td class='row2' align='center' colspan='2'><input type='submit' value='Add'> <input type='reset'></td></tr></table>");
1376: writer.write("</form>");
1377:
1378: writeFooter(writer);
1379: writer.flush();
1380:
1381: }
1382:
1383: private void writeErrors(Writer writer, List errors)
1384: throws IOException {
1385: for (int errorCount = 0; errorCount < errors.size(); errorCount++) {
1386: writer.write(errors.get(errorCount) + "<br>");
1387: }
1388: }
1389:
1390: private void cmdAddChannel(HttpServletRequest request,
1391: HttpServletResponse response) throws ServletException,
1392: IOException {
1393:
1394: ChannelManager channelManager = (ChannelManager) getServletContext()
1395: .getAttribute(AdminServer.SERVLET_CTX_RSS_MANAGER);
1396:
1397: String name = request.getParameter("name").trim();
1398: String urlString = request.getParameter("url").trim();
1399: boolean historical = request.getParameter("historical")
1400: .equalsIgnoreCase("true");
1401: String validateStr = request.getParameter("validate");
1402: boolean validate = false;
1403: if (validateStr == null) {
1404: validate = false;
1405: } else if (validateStr.equalsIgnoreCase("on")) {
1406: validate = true;
1407: }
1408:
1409: List errors = new ArrayList();
1410: if (name.length() == 0) {
1411: errors.add("Name cannot be empty");
1412: } else if (name.indexOf(' ') > -1) {
1413: errors.add("Name cannot contain spaces");
1414: } else if (channelManager.channelByName(name) != null) {
1415: errors.add("Name is already is use");
1416: }
1417:
1418: if (urlString.length() == 0) {
1419: errors.add("URL cannot be empty");
1420: } else if (urlString.equals("http://")) {
1421: errors.add("You must specify a URL");
1422: } else if (!urlString.startsWith("http://")) {
1423: errors.add("Only URLs starting http:// are supported");
1424: }
1425:
1426: Channel newChannel = null;
1427: if (errors.size() == 0) {
1428: try {
1429: newChannel = new Channel(name, urlString);
1430: newChannel.setHistorical(historical);
1431: if (validate && !newChannel.isValid()) {
1432: errors
1433: .add("URL does not point to valid RSS document");
1434: errors
1435: .add("<a target='validate' href='http://feeds.archive.org/validator/check?url="
1436: + urlString
1437: + "'>Check the URL with the RSS Validator @ archive.org</a>");
1438: newChannel = null;
1439: }
1440: } catch (MalformedURLException me) {
1441: errors.add("URL is malformed");
1442: }
1443: }
1444:
1445: Writer writer = response.getWriter();
1446: writeHeader(writer);
1447:
1448: if (errors.size() > 0) {
1449: writer
1450: .write("<b>There were errors adding your channel:</b><p>");
1451: writeErrors(writer, errors);
1452: writer.write("<p>");
1453: writer.write("<form action='/?action=add' method='post'>");
1454: writer.write("<table>");
1455: writer
1456: .write("<tr><td align='right'>Newsgroup Name:</td><td><input type='text' name='name' size='64' value='"
1457: + HTMLHelper.escapeString(name)
1458: + "'></td></tr>");
1459: writer
1460: .write("<tr><td align='right'>RSS URL:</td><td><input type='text' name='url' size='64' value='"
1461: + HTMLHelper.escapeString(urlString)
1462: + "'></td></tr>");
1463: writer
1464: .write("<tr><td align='right'>Historical</td><td><select name='historical'>"
1465: + "<option "
1466: + (historical ? "selected" : "")
1467: + ">true"
1468: + "<option "
1469: + (historical ? "selected" : "")
1470: + ">false"
1471: + "</select></td></tr>");
1472:
1473: writer
1474: .write("<tr><td align='right' valign='top'>Validate</td><td><input type='checkbox' name='validate' "
1475: + (validate ? "checked" : "")
1476: + ">"
1477: + "<br><i>(Checked = Ensure URL points to a valid RSS document)</i></td></tr>");
1478:
1479: writer
1480: .write("<tr><td align='center' colspan='2'><input type='submit' value='Add'> <input type='reset'></td></tr></table>");
1481: writer.write("</form>");
1482: } else {
1483: channelManager.addChannel(newChannel);
1484:
1485: writer.write("Channel " + newChannel.getName()
1486: + " successfully added.");
1487: }
1488:
1489: writeFooter(writer);
1490: writer.flush();
1491:
1492: }
1493:
1494: private void cmdExportChannelConfig(HttpServletRequest request,
1495: HttpServletResponse response) throws ServletException,
1496: IOException {
1497:
1498: // response.setContentType("text/xml");
1499: response.setContentType("application/octet-stream");
1500: response.setHeader("Content-Disposition",
1501: "attachment; filename=\"nntprss-channels.xml\"");
1502: PrintWriter writer = new PrintWriter(response.getWriter());
1503: writer.println("<?xml version='1.0' encoding='UTF-8'?>");
1504: writer.println();
1505: writer.println("<!-- Generated on " + new Date().toString()
1506: + " -->");
1507: writer.println("<nntprss-channels nntprss-version='"
1508: + XMLHelper.escapeString(AppConstants.VERSION) + "'>");
1509:
1510: ChannelManager channelManager = (ChannelManager) getServletContext()
1511: .getAttribute(AdminServer.SERVLET_CTX_RSS_MANAGER);
1512:
1513: Iterator channelIter = channelManager.channels();
1514: while (channelIter.hasNext()) {
1515: Channel channel = (Channel) channelIter.next();
1516: writer.print(" <channel name='");
1517: writer.print(XMLHelper.escapeString(channel.getName()));
1518: writer.print("' url='");
1519: writer.print(XMLHelper.escapeString(channel.getUrl()));
1520: writer.print("' historical='");
1521: writer.print(channel.isHistorical() ? "true" : "false");
1522: writer.println("'/>");
1523: }
1524:
1525: writer.println("</nntprss-channels>");
1526: }
1527:
1528: private void writeImportForm(Writer writer) throws IOException {
1529: writer
1530: .write("<form action='?action=import' method='POST' enctype='multipart/form-data'>");
1531: writer.write("<table class='tableBorder'>");
1532:
1533: writer
1534: .write("<tr><th colspan='2' class='tableHead'>Import Channel List</th></tr>");
1535:
1536: writer
1537: .write("<tr><td class='row1' align='right'>Channel List File</td>");
1538: writer
1539: .write("<td class='row2'><input type='file' name='file'></td></tr>");
1540: writer.write("<tr><td class='row1' align='right'>Format</td>");
1541: writer
1542: .write("<td class='row2'><input type='radio' name='type' value='nntp' checked>nntp//rss Channel List<br>"
1543: + "<input type='radio' name='type' value='opml'>OPML (mySubscriptions.opml)</td></tr>");
1544:
1545: writer
1546: .write("<tr><td class='row2' align='center' colspan='2'><input type='submit' value='Import'></td></tr>");
1547: writer.write("</table>");
1548: writer.write("</form>");
1549: }
1550:
1551: private void cmdImportChannelConfigForm(HttpServletRequest request,
1552: HttpServletResponse response) throws ServletException,
1553: IOException {
1554:
1555: Writer writer = response.getWriter();
1556: writeHeader(writer);
1557:
1558: writeImportForm(writer);
1559:
1560: writeFooter(writer);
1561: writer.flush();
1562:
1563: }
1564:
1565: private void cmdImportChannelConfig(HttpServletRequest request,
1566: HttpServletResponse response) throws ServletException,
1567: IOException {
1568:
1569: MultiPartRequest mpRequest = new MultiPartRequest(request);
1570:
1571: if (mpRequest.getString("type").equalsIgnoreCase("opml")) {
1572: cmdImportOpmlChannelConfigValidate(request, response,
1573: mpRequest);
1574: } else {
1575: cmdImportNntpRssChannelConfig(request, response, mpRequest);
1576: }
1577: }
1578:
1579: private void cmdImportNntpRssChannelConfig(
1580: HttpServletRequest request, HttpServletResponse response,
1581: MultiPartRequest mpRequest) throws ServletException,
1582: IOException {
1583:
1584: Writer writer = response.getWriter();
1585: writeHeader(writer);
1586:
1587: ChannelManager channelManager = (ChannelManager) getServletContext()
1588: .getAttribute(AdminServer.SERVLET_CTX_RSS_MANAGER);
1589:
1590: writer.write("<b>Import status</b><p>");
1591:
1592: List errors = new ArrayList();
1593: int channelsAdded = 0;
1594:
1595: // Parse XML
1596: try {
1597: DocumentBuilder db = AppConstants.newDocumentBuilder();
1598: Document doc = db.parse(mpRequest.getInputStream("file"));
1599: Element docElm = doc.getDocumentElement();
1600: NodeList channels = docElm.getElementsByTagName("channel");
1601:
1602: for (int channelCount = 0; channelCount < channels
1603: .getLength(); channelCount++) {
1604: Element chanElm = (Element) channels.item(channelCount);
1605:
1606: String name = chanElm.getAttribute("name");
1607: String urlString = chanElm.getAttribute("url");
1608: boolean historical = false;
1609: String historicalStr = chanElm
1610: .getAttribute("historical");
1611: if (historicalStr != null) {
1612: historical = historicalStr.equalsIgnoreCase("true");
1613: }
1614:
1615: // Check name...
1616: List currentErrors = new ArrayList();
1617: Channel existingChannel = channelManager
1618: .channelByName(name);
1619:
1620: if (name.length() == 0) {
1621: currentErrors.add("Channel with empty name - URL="
1622: + urlString);
1623: } else if (name.indexOf(' ') > -1) {
1624: currentErrors
1625: .add("Channel name cannot contain spaces - name="
1626: + name);
1627: } else if (existingChannel != null) {
1628: currentErrors.add("Channel name " + name
1629: + " is already is use");
1630: }
1631:
1632: if (urlString.length() == 0) {
1633: currentErrors
1634: .add("URL cannot be empty, channel name="
1635: + name);
1636: } else if (urlString.equals("http://")) {
1637: currentErrors
1638: .add("You must specify a URL, channel name="
1639: + name);
1640: } else if (!urlString.startsWith("http://")) {
1641: currentErrors
1642: .add("Only URLs starting http:// are supported, channel name="
1643: + name + ", url=" + urlString);
1644: }
1645:
1646: if (existingChannel == null) {
1647:
1648: Channel newChannel = null;
1649: if (currentErrors.size() == 0) {
1650: try {
1651: newChannel = new Channel(name, urlString);
1652: newChannel.setHistorical(historical);
1653: channelManager.addChannel(newChannel);
1654: channelsAdded++;
1655: } catch (MalformedURLException me) {
1656: errors.add("Channel " + name + " - URL ("
1657: + urlString + ") is malformed");
1658: }
1659: }
1660:
1661: // Removed channel validation... channels will be validated
1662: // on next iteration of channel poller - will be highlighted
1663: // in channel list if invalid
1664: // Validate channel...
1665: // if(Channel.isValid(new URL(urlString))) {
1666: //// Add channel...
1667: // Channel newChannel = null;
1668: // if(currentErrors.size() == 0) {
1669: // try {
1670: // newChannel = new Channel(name, urlString);
1671: // newChannel.setHistorical(historical);
1672: // channelManager.addChannel(newChannel);
1673: // channelsAdded++;
1674: // } catch(MalformedURLException me) {
1675: // errors.add("Channel " + name + " - URL ("
1676: // + urlString + ") is malformed");
1677: // }
1678: // }
1679: //
1680: // } else {
1681: //// URL points to invalid document
1682: // errors.add("Channel " + name + "'s URL (" + urlString + ") "
1683: // + "points to an invalid document");
1684: // }
1685: }
1686:
1687: errors.addAll(currentErrors);
1688:
1689: }
1690: } catch (SAXException se) {
1691: errors
1692: .add("There was an error parsing your channel file:<br>"
1693: + se.getMessage());
1694: } catch (ParserConfigurationException pce) {
1695: errors
1696: .add("There was a problem reading your channelf file:<br>"
1697: + pce.getMessage());
1698: }
1699:
1700: // Display any errors encountered during parsing...
1701: if (errors.size() > 0) {
1702: writer
1703: .write("Problems were encountered while adding channels.<p>");
1704: writeErrors(writer, errors);
1705:
1706: if (channelsAdded > 0) {
1707: writer.write("<p>" + channelsAdded
1708: + " channel(s) were successfully imported.");
1709: }
1710: } else {
1711: if (channelsAdded > 0) {
1712: writer.write("<p>" + channelsAdded
1713: + " channel(s) were successfully imported.");
1714: } else {
1715: writer
1716: .write("The configuration file did not contain any channels!");
1717: }
1718: }
1719:
1720: writeFooter(writer);
1721: writer.flush();
1722:
1723: }
1724:
1725: private String fixChannelName(String channelName) {
1726: StringBuffer fixedName = new StringBuffer();
1727:
1728: for (int i = 0; i < channelName.length(); i++) {
1729: char c = channelName.charAt(i);
1730: if (Character.isWhitespace(c)) {
1731: fixedName.append("_");
1732: } else {
1733: fixedName.append(c);
1734: }
1735: }
1736: return fixedName.toString().trim();
1737: }
1738:
1739: private String createChannelName(String urlString) {
1740: String name = null;
1741: try {
1742: URL url = new URL(urlString);
1743: String host = url.getHost();
1744: Stack hostStack = new Stack();
1745: StringTokenizer hostTok = new StringTokenizer(host, ".");
1746: while (hostTok.hasMoreTokens()) {
1747: String token = hostTok.nextToken();
1748: hostStack.push(token);
1749: }
1750:
1751: StringBuffer channelName = new StringBuffer();
1752: while (hostStack.size() > 0) {
1753: channelName.append(hostStack.pop());
1754: if (hostStack.size() > 0) {
1755: channelName.append('.');
1756: }
1757: }
1758:
1759: name = channelName.toString();
1760:
1761: } catch (MalformedURLException mue) {
1762: name = "Unnamed_Channel_" + System.currentTimeMillis();
1763: }
1764:
1765: return name;
1766: }
1767:
1768: private void cmdImportOpmlChannelConfigValidate(
1769: HttpServletRequest request, HttpServletResponse response,
1770: MultiPartRequest mpRequest) throws ServletException,
1771: IOException {
1772:
1773: Writer writer = response.getWriter();
1774: writeHeader(writer);
1775:
1776: writeCheckboxSelector(writer, "checkAllImport", "import",
1777: "channels");
1778: writeCheckboxSelector(writer, "checkAllHistorical",
1779: "historical", "channels");
1780:
1781: ChannelManager channelManager = (ChannelManager) getServletContext()
1782: .getAttribute(AdminServer.SERVLET_CTX_RSS_MANAGER);
1783:
1784: writer.write("<b>mySubscriptions.opml validation</b><p>");
1785:
1786: List errors = new ArrayList();
1787:
1788: // Parse XML
1789: try {
1790: DocumentBuilder db = AppConstants.newDocumentBuilder();
1791: Document doc = db.parse(mpRequest.getInputStream("file"));
1792: Element docElm = doc.getDocumentElement();
1793:
1794: // XXX Expectation is that there will be a body element
1795: Element bodyElm = (Element) docElm.getElementsByTagName(
1796: "body").item(0);
1797:
1798: NodeList channels = docElm.getElementsByTagName("outline");
1799: if (channels.getLength() > 0) {
1800:
1801: writer
1802: .write("<form name='channels' action='?action=importopml' method='POST'>");
1803: writer
1804: .write("<table class='tableBorder'><tr><th>Import<br>"
1805: + "<input type='checkbox' name='changeImport' onClick='checkAllImport(this);' checked>"
1806: + "</th><th>Channel Name</th><th>Historical<br>"
1807: + "<input type='checkbox' name='changeHistorical' onClick='checkAllHistorical(this);'>"
1808: + "</th><th>URL</th></tr>");
1809:
1810: for (int channelCount = 0; channelCount < channels
1811: .getLength(); channelCount++) {
1812: Element chanElm = (Element) channels
1813: .item(channelCount);
1814:
1815: String name = fixChannelName(chanElm
1816: .getAttribute("title"));
1817: String urlString = chanElm.getAttribute("xmlUrl");
1818:
1819: if (name.length() == 0) {
1820: name = createChannelName(urlString);
1821: }
1822:
1823: String rowClass = (((channelCount % 2) == 1) ? "row1"
1824: : "row2");
1825:
1826: writer
1827: .write("<tr>" + "<td class='"
1828: + rowClass
1829: + "' align='center'><input type='checkbox' name='import"
1830: + channelCount
1831: + "' checked></td>"
1832: + "<td class='"
1833: + rowClass
1834: + "'><input type='value' size='50' name='name"
1835: + channelCount
1836: + "' value='"
1837: + HTMLHelper.escapeString(name)
1838: + "'></td>"
1839: + "<td class='"
1840: + rowClass
1841: + "' align='center'><input type='checkbox' name='historical"
1842: + channelCount
1843: + "'></td>"
1844: + "<td class='"
1845: + rowClass
1846: + "' ><input type='hidden' name='url"
1847: + channelCount
1848: + "' value='"
1849: + HTMLHelper
1850: .escapeString(urlString)
1851: + "'>"
1852: + HTMLHelper
1853: .escapeString(urlString)
1854: + "</td></tr>\n");
1855: }
1856:
1857: writer
1858: .write("<tr><td align='center' class='row2' colspan='4'><input type='submit' value='Import Channels'></td></tr>");
1859: writer.write("</table></form>");
1860: } else {
1861: writer
1862: .write("Your mySubscriptions.opml file did not contain any channels, or was in an invalid format.<p>");
1863: }
1864:
1865: } catch (SAXException se) {
1866: errors
1867: .add("There was an error parsing your channel file:<br>"
1868: + se.getMessage());
1869: } catch (ParserConfigurationException pce) {
1870: errors
1871: .add("There was a problem reading your channelf file:<br>"
1872: + pce.getMessage());
1873: }
1874:
1875: // Display any errors encountered during parsing...
1876: if (errors.size() > 0) {
1877: writer
1878: .write("Problems were encountered while adding channels.<p>");
1879: writeErrors(writer, errors);
1880: }
1881:
1882: writeFooter(writer);
1883: writer.flush();
1884:
1885: }
1886:
1887: private void cmdImportOpmlChannelConfig(HttpServletRequest request,
1888: HttpServletResponse response) throws ServletException,
1889: IOException {
1890:
1891: Writer writer = response.getWriter();
1892: writeHeader(writer);
1893:
1894: ChannelManager channelManager = (ChannelManager) getServletContext()
1895: .getAttribute(AdminServer.SERVLET_CTX_RSS_MANAGER);
1896:
1897: writer.write("<b>Import status</b><p>");
1898:
1899: List errors = new ArrayList();
1900: int channelsAdded = 0;
1901:
1902: StringBuffer errorContent = new StringBuffer();
1903:
1904: int channelCount = 0;
1905: int newChannelCount = 0;
1906: String urlString = request.getParameter("url" + channelCount);
1907:
1908: while (urlString != null) {
1909:
1910: String addStr = request.getParameter("import"
1911: + channelCount);
1912: if (addStr != null) {
1913:
1914: String name = request.getParameter("name"
1915: + channelCount);
1916: String historicalStr = request
1917: .getParameter("historical" + channelCount);
1918: boolean historical = false;
1919: // FIXME check
1920: if (historicalStr != null) {
1921: historical = true;
1922: }
1923:
1924: // Check name...
1925: List currentErrors = new ArrayList();
1926: Channel existingChannel = channelManager
1927: .channelByName(name);
1928:
1929: if (name.length() == 0) {
1930: currentErrors.add("Channel with empty name - URL="
1931: + urlString);
1932: } else if (name.indexOf(' ') > -1) {
1933: currentErrors
1934: .add("Channel name cannot contain spaces - name="
1935: + name);
1936: } else if (existingChannel != null) {
1937: currentErrors.add("Channel name " + name
1938: + " is already is use");
1939: }
1940:
1941: if (urlString.length() == 0) {
1942: currentErrors
1943: .add("URL cannot be empty, channel name="
1944: + name);
1945: } else if (urlString.equals("http://")) {
1946: currentErrors
1947: .add("You must specify a URL, channel name="
1948: + name);
1949: } else if (!urlString.startsWith("http://")) {
1950: currentErrors
1951: .add("Only URLs starting http:// are supported, channel name="
1952: + name + ", url=" + urlString);
1953: }
1954:
1955: if (existingChannel == null) {
1956:
1957: Channel newChannel = null;
1958: if (currentErrors.size() == 0) {
1959: try {
1960: newChannel = new Channel(name, urlString);
1961: newChannel.setHistorical(historical);
1962: channelManager.addChannel(newChannel);
1963: channelsAdded++;
1964: } catch (MalformedURLException me) {
1965: errors.add("Channel " + name + " - URL ("
1966: + urlString + ") is malformed");
1967: }
1968: }
1969:
1970: }
1971:
1972: if (currentErrors.size() > 0) {
1973: errorContent
1974: .append("<tr>"
1975: + "<td rowspan='2' align='center'><input type='checkbox' name='import"
1976: + newChannelCount
1977: + "' checked></td>"
1978: + "<td><input type='value' size='50' name='name"
1979: + newChannelCount
1980: + "' value='"
1981: + HTMLHelper.escapeString(name)
1982: + "'></td>"
1983: + "<td align='center'><input type='checkbox' name='historical"
1984: + newChannelCount
1985: + "' "
1986: + (historical ? "checked" : "")
1987: + "></td>"
1988: + "<td><input type='hidden' name='url"
1989: + newChannelCount++
1990: + "' value='"
1991: + HTMLHelper
1992: .escapeString(urlString)
1993: + "'>"
1994: + HTMLHelper
1995: .escapeString(urlString)
1996: + "</td></tr>\n");
1997: errorContent
1998: .append("<tr><td colspan='3'> <font color='red'>");
1999: for (Iterator i = currentErrors.iterator(); i
2000: .hasNext();) {
2001: String error = (String) i.next();
2002: errorContent.append(error);
2003: errorContent.append("<br>\n");
2004: }
2005: errorContent.append("</font></td></tr>\n");
2006: }
2007: } // end import != null
2008:
2009: channelCount++;
2010: urlString = request.getParameter("url" + channelCount);
2011:
2012: }
2013:
2014: // Display any errors encountered during processing...
2015: if (errorContent.length() > 0) {
2016: if (channelsAdded > 0) {
2017: writer.write("<p>" + channelsAdded
2018: + " channel(s) were successfully imported.");
2019: }
2020:
2021: writeCheckboxSelector(writer, "checkAllImport", "import",
2022: "channels");
2023: writeCheckboxSelector(writer, "checkAllHistorical",
2024: "historical", "channels");
2025:
2026: writer
2027: .write("Problems were encountered while adding channels.<p>");
2028: writer
2029: .write("<form name='channels' action='?action=importopml' method='POST'>");
2030: writer
2031: .write("<table border='1'><tr><th>Import<br>"
2032: + "<input type='checkbox' name='changeImport' onClick='checkAllImport(this);' checked>"
2033: + "</th><th>Channel Name</th><th>Historical<br>"
2034: + "<input type='checkbox' name='changeHistorical' onClick='checkAllHistorical(this);'>"
2035: + "</th><th>URL</th></tr>");
2036: writer.write(errorContent.toString());
2037: writer.write("</table>");
2038: writer
2039: .write("<br><input type='submit' value='Import Channels'></form>");
2040:
2041: } else {
2042: if (channelsAdded > 0) {
2043: writer.write("<p>" + channelsAdded
2044: + " channel(s) were successfully imported.");
2045: } else {
2046: writer.write("No channels were selected for import!");
2047: }
2048: }
2049:
2050: writeFooter(writer);
2051: writer.flush();
2052:
2053: }
2054:
2055: private void cmdHelp(HttpServletRequest request,
2056: HttpServletResponse response) throws ServletException,
2057: IOException {
2058:
2059: Writer writer = response.getWriter();
2060: writeHeader(writer);
2061:
2062: writer
2063: .write("<table class='tableborder' border='0' width='80%'>");
2064: writer.write("<tr><th class='tableHead'>Help</td></th>");
2065:
2066: writer.write("<tr><th class='subHead'>Latest News</th></tr>");
2067: writer
2068: .write("<tr><td><br>The latest nntp//rss project news can always be found on the <a href='http://www.methodize.org/nntprss'>nntp//rss project page</a>.<br><br></td></tr>");
2069:
2070: writer
2071: .write("<tr><th class='subHead'>Using the Administration Interface</th></tr>");
2072: writer
2073: .write("<tr><td>"
2074: + "<br><b>Add Channel</b></p>"
2075: + "Channels can be added to nntp//rss through this screen.<p><ul>"
2076: + "<li>Newsgroup name - name of the NNTP newsgroup that this RSS feed will appear as in your newsreader"
2077: + "<li>URL - URL for the RSS feed"
2078: + "<li>Historical - keep items once they have been removed from the original feed"
2079: + "<li>Validate - check the URL for a validate RSS document when adding to the list of channels. You may want to disable this validation if the remote feed is temporarily unavailable, but you still wish to add it to the list."
2080: + "</ul>"
2081: + "<p><b>View Channels</b><p>"
2082: + "This screen provides an overview of configured channels, their status, and the date and time of the last poll.<p>"
2083: + "Channel Status<p>"
2084: + "<table border='0'>"
2085: + "<tr><td class='row1'><b>OK</b></td><td class='row2'>Last poll was successful.</td></tr>"
2086: + "<tr><td class='chldisabled'><b>Disabled</b></td><td class='row2'>Channel has been disabled.</td></tr>"
2087: + "<tr><td class='chlwarning'><b>Warning</b></td><td class='row2'>nntp//rss is was unable to contact the channel's web server on the last poll. This usually indicates an temporary problem, generally resolved on the next poll.</td></tr>"
2088: + "<tr><td class='chlerror'><b>Error</b></td><td class='row2'>A significant error occured when polling. This may either be that the RSS feed no longer exists, or that it is badly formatted. In the case of the latter, enabling <i>Parse-at-all-cost</i> within the channel's configuration may provide a resolution</td></tr>"
2089: + "</table><p>"
2090: + "Clicking on the name of a channel will display the channel's configuration screen.<p>"
2091: + "Clicking on the [Read] button will open the channel in your default newsreader.<p>"
2092: + "<b>Channel Configuration</b><p>"
2093: + "Within this screen you can:"
2094: + "<ul><li>Modify the URL for the channel,"
2095: + "<li>Enable or disable polling of the channel,"
2096: + "<li>Set a channel-specific polling interval, or use the system wide default (set in <i>System Configuration</i>),"
2097: + "<li>Enable the Parse-at-all-cost parser. This experimental parser will parse RSS feeds that are not well-formed XML,"
2098: + "<li>Change the historical status of the channel. If a channel is 'historical', items that are removed from the RSS feed will be kept within nntp//rss's database,"
2099: + "<li>Enabled posting (See below)."
2100: + "</ul>"
2101: + "<p><b>Posting</b><p>"
2102: + "nntp//rss supports the <a href='http://plant.blogger.com/api/index.html'>Blogger</a>, <a href='http://www.xmlrpc.com/metaWeblogApi'>MetaWeblog</a> and <a href='http://www.livejournal.com/doc/server/ljp.csp.xml-rpc.protocol.html'>LiveJournal</a> APIs, allow you to publish directly to your blog from within your NNTP newsreader. Just configure your blog's nntp//rss channel for posting, then use your newsreader's native posting capability to post to the group. Your post will be immediately sent to your blog's host for publication.<p>"
2103: + "Posting is configured within the channel's configuration screen. Enabling posting on a channel will display an additional set of configuration fields. With these fields you provide the information required by your blog's publishing API.<p>"
2104: + "<b>Note: It is recommended that you enable <a href='#SecureNNTP'>Secure NNTP</a> if you enable posting. This will ensure that only an authenticated newsreader will be able to post to the blog.</b>"
2105: + "<p><b>System Configuration</b><p>"
2106: + "System wide configuration is managed on this screen.<p>"
2107: + "<i>Channel Polling Interval</i> - this determines how often nntp//rss will check RSS feeds for updates. This can be set as low as ten minutes, but sixty minutes should be sufficient for most feeds.<p>"
2108: + "<i>Content Type</i> - By default, nntp//rss serves messages to newsreaders in a combined (multipart/alternative) plain text and HTML MIME format. This means that each message contains both a plain text and an HTML version of the item. If you a using an older newsreader that does not support this mode, you may want to change the content type to Text (text/plain).<p>"
2109: + "<i>Proxy</i> - If you are running nntp//rss behind a proxy, you may need to configure your proxy settings here. Proxy host name, port and an optional username and password can be specified. Leave these fields blank if you are not behind a proxy.<p>"
2110: + "<i><a name='SecureNNTP'>Secure NNTP</a></i>, when enabled, will require NNTP newsreaders to authenticate themselves before being allowed to read or post. This uses the same user information as used for securing the web interface.<p>"
2111: + "Import and Export of Channel Lists<p>"
2112: + "nntp//rss supports the import and export of channel lists. For import, both nntp//rss and OPML (mySubscription.opml) format lists are supported. Click on <i>Import Channel List</i>, select the file containing the subscription list, ensuring the correct type of file is checked, then click on the <i>Import</i> button. For OPML files, nntp//rss will automatically generate a newsgroup name. You will be able to modify these names, and also select the subset of channels to import, before the process is finalized.<p>");
2113:
2114: writer
2115: .write("<tr><th class='subHead'>Securing access to nntp//rss</th></tr>");
2116:
2117: writer
2118: .write("<tr><td><br>Access to the web admin interface can be password protected by creating a file named users.properties in the root directory of your nntp//rss installation.<p>"
2119: + "This file takes the form:<p><code>username:password</code><p>e.g. sample user.properties<p><code>jason:mypassword</code><p>"
2120: + "Multiple users can be configured by adding additional lines of usernames and passwords in the above format.<p>"
2121: + "You will need to stop and restart nntp//rss for these changes to take effect. The next time you access the web interface, you will be prompted to enter your user name and password.<p>"
2122: + "To disable secure access, just delete or rename the users.properties file, and stop and restart nntp//rss.<p>"
2123: + "If you wish to secure access to the NNTP interface, just enable <i>Secure NNTP</i> within the System Configuration screen.<br><p></td></tr>");
2124:
2125: writer.write("<tr><th class='subHead'>Windows Users</th></tr>");
2126:
2127: writer
2128: .write("<tr><td><br>Read the <a href='http://www.methodize.org/nntprss/docs/WINDOWS-SERVICE.TXT'>WINDOWS-SERVICE.TXT</a> file, located in the installation directory, for information on how to configure nntp//rss to run as a Windows service.<p>"
2129: + "nntp//rss has a System Tray icon when run interactively on Windows. This provides quick access to the administration interface, as well as the ability to easily shut-down nntp//rss. "
2130: + "Just right click on the 'N' icon, and choose your option. You can also double click on the icon to bring up the administrative interface.<p>");
2131:
2132: writer.write("</td></tr>");
2133:
2134: writer
2135: .write("<tr><th class='subHead'><span class='smalltext'>nntp//rss - Copyright © 2002-2003 Jason Brome. All Rights Reserved.</span></th></tr>");
2136:
2137: writer.write("</table>");
2138:
2139: writeFooter(writer);
2140: writer.flush();
2141: }
2142:
2143: private void processRequest(HttpServletRequest request,
2144: HttpServletResponse response) throws ServletException,
2145: IOException {
2146:
2147: response.setContentType("text/html");
2148:
2149: String action = request.getParameter("action");
2150: if (action == null || action.length() == 0) {
2151: cmdShowCurrentChannels(request, response);
2152: } else if (action.equals("add")) {
2153: cmdAddChannel(request, response);
2154: } else if (action.equals("addform")) {
2155: cmdAddChannelForm(request, response);
2156: } else if (action.equals("showconfig")) {
2157: cmdShowConfig(request, response);
2158: } else if (action.equals("updateconfig")) {
2159: cmdUpdateConfig(request, response);
2160: } else if (action.equals("show")) {
2161: cmdShowChannel(request, response);
2162: } else if (action.equals("update")) {
2163: cmdUpdateChannel(request, response);
2164: } else if (action.equals("export")) {
2165: cmdExportChannelConfig(request, response);
2166: } else if (action.equals("import")) {
2167: cmdImportChannelConfig(request, response);
2168: } else if (action.equals("importform")) {
2169: cmdImportChannelConfigForm(request, response);
2170: } else if (action.equals("importopml")) {
2171: cmdImportOpmlChannelConfig(request, response);
2172: } else if (action.equals("channelaction")) {
2173: cmdChannelAction(request, response);
2174: } else if (action.equals("editchlrefresh")) {
2175: cmdEditChannelRefresh(request, response);
2176: } else if (action.equals("help")) {
2177: cmdHelp(request, response);
2178: } else {
2179: cmdShowCurrentChannels(request, response);
2180: }
2181:
2182: }
2183:
2184: /**
2185: * @see javax.servlet.http.HttpServlet#doGet(HttpServletRequest, HttpServletResponse)
2186: */
2187: protected void doGet(HttpServletRequest request,
2188: HttpServletResponse response) throws ServletException,
2189: IOException {
2190:
2191: processRequest(request, response);
2192: }
2193:
2194: /**
2195: * @see javax.servlet.http.HttpServlet#doPost(HttpServletRequest, HttpServletResponse)
2196: */
2197: protected void doPost(HttpServletRequest request,
2198: HttpServletResponse response) throws ServletException,
2199: IOException {
2200:
2201: processRequest(request, response);
2202: }
2203:
2204: }
|