diff options
23 files changed, 706 insertions, 44 deletions
diff --git a/nbproject/build-impl.xml b/nbproject/build-impl.xml index 267f24af..4d5df024 100644 --- a/nbproject/build-impl.xml +++ b/nbproject/build-impl.xml @@ -535,6 +535,9 @@ exists or setup the property manually. For example like this: <ant antfile="${project.com_juick_server}/build.xml" inheritall="false" target="jar"> <property name="deploy.on.save" value="false"/> </ant> + <ant antfile="${project.com_juick_xmpp}/build.xml" inheritall="false" target="jar"> + <property name="deploy.on.save" value="false"/> + </ant> </target> <target depends="init" if="dist.ear.dir" name="deps-ear-jar" unless="no.deps"> <ant antfile="${project.com_juick}/build.xml" inheritall="false" target="jar"> @@ -543,6 +546,9 @@ exists or setup the property manually. For example like this: <ant antfile="${project.com_juick_server}/build.xml" inheritall="false" target="jar"> <property name="deploy.on.save" value="false"/> </ant> + <ant antfile="${project.com_juick_xmpp}/build.xml" inheritall="false" target="jar"> + <property name="deploy.on.save" value="false"/> + </ant> </target> <target depends="init, deps-module-jar, deps-ear-jar" name="deps-jar" unless="no.deps"/> <target depends="init,deps-jar" name="-pre-pre-compile"> @@ -689,12 +695,14 @@ exists or setup the property manually. For example like this: <target depends="init" if="dist.ear.dir" name="library-inclusion-in-manifest"> <copyfiles files="${reference.com_juick.jar}" iftldtodir="${build.web.dir}/WEB-INF" todir="${dist.ear.dir}/lib"/> <copyfiles files="${reference.com_juick_server.jar}" iftldtodir="${build.web.dir}/WEB-INF" todir="${dist.ear.dir}/lib"/> + <copyfiles files="${reference.com_juick_xmpp.jar}" iftldtodir="${build.web.dir}/WEB-INF" todir="${dist.ear.dir}/lib"/> <mkdir dir="${build.web.dir}/META-INF"/> <manifest file="${build.web.dir}/META-INF/MANIFEST.MF" mode="update"/> </target> <target depends="init" name="library-inclusion-in-archive" unless="dist.ear.dir"> <copyfiles files="${reference.com_juick.jar}" todir="${build.web.dir}/WEB-INF/lib"/> <copyfiles files="${reference.com_juick_server.jar}" todir="${build.web.dir}/WEB-INF/lib"/> + <copyfiles files="${reference.com_juick_xmpp.jar}" todir="${build.web.dir}/WEB-INF/lib"/> </target> <target depends="init" if="dist.ear.dir" name="-clean-webinf-lib"> <delete dir="${build.web.dir}/WEB-INF/lib"/> @@ -1061,6 +1069,7 @@ exists or setup the property manually. For example like this: <target depends="init" name="deps-clean" unless="no.deps"> <ant antfile="${project.com_juick}/build.xml" inheritall="false" target="clean"/> <ant antfile="${project.com_juick_server}/build.xml" inheritall="false" target="clean"/> + <ant antfile="${project.com_juick_xmpp}/build.xml" inheritall="false" target="clean"/> </target> <target depends="init" name="do-clean"> <condition property="build.dir.to.clean" value="${build.web.dir}"> diff --git a/nbproject/genfiles.properties b/nbproject/genfiles.properties index e8e9295a..ba8ca356 100644 --- a/nbproject/genfiles.properties +++ b/nbproject/genfiles.properties @@ -1,8 +1,8 @@ -build.xml.data.CRC32=9ff506d8 +build.xml.data.CRC32=8d999c58 build.xml.script.CRC32=c93fa366 build.xml.stylesheet.CRC32=651128d4@1.33.1.1 # This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml. # Do not edit this file. You may delete it but then the IDE will never regenerate such files for you. -nbproject/build-impl.xml.data.CRC32=9ff506d8 -nbproject/build-impl.xml.script.CRC32=e315adae +nbproject/build-impl.xml.data.CRC32=8d999c58 +nbproject/build-impl.xml.script.CRC32=9d8ae4f2 nbproject/build-impl.xml.stylesheet.CRC32=0cbf5bb7@1.33.1.1 diff --git a/nbproject/project.properties b/nbproject/project.properties index de13c257..873bcd4c 100644 --- a/nbproject/project.properties +++ b/nbproject/project.properties @@ -36,7 +36,8 @@ jar.compress=false javac.classpath=\ ${reference.com_juick.jar}:\ ${reference.com_juick_server.jar}:\ - ${libs.MySQLDriver.classpath} + ${libs.MySQLDriver.classpath}:\ + ${reference.com_juick_xmpp.jar} # Space-separated list of extra javac options javac.compilerargs= javac.debug=true @@ -67,8 +68,10 @@ persistence.xml.dir=${conf.dir} platform.active=default_platform project.com_juick=../com.juick project.com_juick_server=../com.juick.server +project.com_juick_xmpp=../com.juick.xmpp reference.com_juick.jar=${project.com_juick}/dist/com.juick.jar reference.com_juick_server.jar=${project.com_juick_server}/dist/com.juick.server.jar +reference.com_juick_xmpp.jar=${project.com_juick_xmpp}/dist/com.juick.xmpp.jar resource.dir=setup run.test.classpath=\ ${javac.test.classpath}:\ diff --git a/nbproject/project.xml b/nbproject/project.xml index 78c0648d..45861aa2 100644 --- a/nbproject/project.xml +++ b/nbproject/project.xml @@ -17,6 +17,10 @@ <library dirs="200"> <file>${libs.MySQLDriver.classpath}</file> </library> + <library dirs="200"> + <file>${reference.com_juick_xmpp.jar}</file> + <path-in-war>WEB-INF/lib</path-in-war> + </library> </web-module-libraries> <web-module-additional-libraries/> <source-roots> @@ -43,6 +47,14 @@ <clean-target>clean</clean-target> <id>jar</id> </reference> + <reference> + <foreign-project>com_juick_xmpp</foreign-project> + <artifact-type>jar</artifact-type> + <script>build.xml</script> + <target>jar</target> + <clean-target>clean</clean-target> + <id>jar</id> + </reference> </references> </configuration> </project> diff --git a/src/java/Global.properties b/src/java/Global.properties index 0883bec5..ebede501 100644 --- a/src/java/Global.properties +++ b/src/java/Global.properties @@ -15,3 +15,5 @@ Help=Help Older=Older Newer=Newer (replies)\ by=by +Comment=Comment +Sponsored\ by=Sponsored by diff --git a/src/java/Global_ru.properties b/src/java/Global_ru.properties index 81ba9c4f..34d7418a 100644 --- a/src/java/Global_ru.properties +++ b/src/java/Global_ru.properties @@ -15,3 +15,5 @@ Help=\u0421\u043f\u0440\u0430\u0432\u043a\u0430 Older=\u0421\u0442\u0430\u0440\u044b\u0435 Newer=\u041d\u043e\u0432\u044b\u0435 (replies)\ by=\u043e\u0442 +Comment=\u041e\u0442\u0432\u0435\u0442\u0438\u0442\u044c +Sponsored\ by=\u041f\u0440\u0438 \u043f\u043e\u0434\u0434\u0435\u0440\u0436\u043a\u0435 diff --git a/src/java/NewMessage.properties b/src/java/NewMessage.properties new file mode 100644 index 00000000..93d91111 --- /dev/null +++ b/src/java/NewMessage.properties @@ -0,0 +1,13 @@ +# To change this template, choose Tools | Templates +# and open the template in the editor. + +New\ message=New message +Location=Location +Clear=Clear +Attachment=Attachment +from\ webcam=from webcam +Webcam\ photo=Webcam photo +Post=Post +Tags=Tags +Photo_JPG=Photo: JPG, up to 10Mb. Video: MP4/3GP/MOV/WMV/AVI/M4V, up to 100Mb. +or=or diff --git a/src/java/NewMessage_ru.properties b/src/java/NewMessage_ru.properties new file mode 100644 index 00000000..bb61fe23 --- /dev/null +++ b/src/java/NewMessage_ru.properties @@ -0,0 +1,14 @@ +# To change this template, choose Tools | Templates +# and open the template in the editor. + +New\ message=\u041d\u043e\u0432\u043e\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 +Location=\u041c\u0435\u0441\u0442\u043e\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 +Clear=\u0423\u0434\u0430\u043b\u0438\u0442\u044c +Attachment=\u0424\u0430\u0439\u043b + +from\ webcam=\u0441 \u0432\u0435\u0431\u043a\u0430\u043c\u0435\u0440\u044b +Webcam\ photo=\u0424\u043e\u0442\u043e \u0441 \u0432\u0435\u0431\u043a\u0430\u043c\u0435\u0440\u044b +Post=\u041e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c +Tags=\u0422\u0435\u0433\u0438 +Photo_JPG=\u0424\u043e\u0442\u043e: JPG, \u0434\u043e 10\u041c\u0431. \u0412\u0438\u0434\u0435\u043e: MP4/3GP/MOV/WMV/AVI/M4V, \u0434\u043e 100Mb. +or=\u0438\u043b\u0438 diff --git a/src/java/com/juick/http/www/Blogs.java b/src/java/com/juick/http/www/Blogs.java index 3b9b6882..b4d30de0 100644 --- a/src/java/com/juick/http/www/Blogs.java +++ b/src/java/com/juick/http/www/Blogs.java @@ -160,7 +160,7 @@ public class Blogs { out.println("</div>"); out.println("</div>"); - PageTemplates.pageFooter(out, locale); + PageTemplates.pageFooter(request, out, locale, visitor); } finally { out.close(); } diff --git a/src/java/com/juick/http/www/Chats.java b/src/java/com/juick/http/www/Chats.java index 3df37976..d15bb271 100644 --- a/src/java/com/juick/http/www/Chats.java +++ b/src/java/com/juick/http/www/Chats.java @@ -69,7 +69,7 @@ public class Chats { out.println("</ul></div>"); out.println("</div>"); - PageTemplates.pageFooter(out, locale); + PageTemplates.pageFooter(request, out, locale, visitor); } finally { out.close(); } diff --git a/src/java/com/juick/http/www/Login.java b/src/java/com/juick/http/www/Login.java index d03dd64d..1baf6e43 100644 --- a/src/java/com/juick/http/www/Login.java +++ b/src/java/com/juick/http/www/Login.java @@ -57,7 +57,7 @@ public class Login { out.println("</div>"); out.println("</div>"); - PageTemplates.pageFooter(out, locale); + PageTemplates.pageFooter(request, out, locale, visitor); } finally { out.close(); } @@ -98,7 +98,12 @@ public class Login { c.setMaxAge(0); response.addCookie(c); - response.sendRedirect("/"); + String referer = request.getHeader("Referer"); + if (referer != null && referer.startsWith("http://juick.com/") && !referer.equals("http://juick.com/login")) { + response.sendRedirect(referer); + } else { + response.sendRedirect("/"); + } } else { response.sendError(403); } diff --git a/src/java/com/juick/http/www/Main.java b/src/java/com/juick/http/www/Main.java index 709c6588..b7f95a4a 100644 --- a/src/java/com/juick/http/www/Main.java +++ b/src/java/com/juick/http/www/Main.java @@ -17,6 +17,7 @@ */ package com.juick.http.www; +import com.juick.xmpp.*; import java.io.FileInputStream; import java.io.IOException; import java.sql.Connection; @@ -28,16 +29,18 @@ import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import ru.sape.Sape; /** * * @author Ugnich Anton */ @WebServlet(name = "Main", urlPatterns = {"/"}) -public class Main extends HttpServlet { +public class Main extends HttpServlet implements XmppListener { Connection sql; Connection sqlSearch; + XmppConnection xmpp; Blogs blogs = new Blogs(); Chats chats = new Chats(); Photos photos = new Photos(); @@ -58,6 +61,13 @@ public class Main extends HttpServlet { Class.forName("com.mysql.jdbc.Driver"); sql = DriverManager.getConnection("jdbc:mysql://localhost/juick?autoReconnect=true&user=" + conf.getProperty("mysql_username", "") + "&password=" + conf.getProperty("mysql_password", "")); sqlSearch = DriverManager.getConnection("jdbc:mysql://127.0.0.1:9306/juick?autoReconnect=true&characterEncoding=utf8&maxAllowedPacket=512000&relaxAutoCommit=true&user=root&password="); + /* + xmpp = new XmppConnectionComponent(new JID("www.juick.com"), conf.getProperty("xmpp_password", ""), "127.0.0.1", 5347, false); + xmpp.addListener((XmppListener) this); + xmpp.start(); + */ + + PageTemplates.sape = new Sape(conf.getProperty("sape_user"), "juick.com", 2000, 3600); } catch (Exception e) { log(null, e); } @@ -69,10 +79,34 @@ public class Main extends HttpServlet { if (sql != null) { try { sql.close(); + sql = null; } catch (SQLException e) { log(null, e); } } + if (sqlSearch != null) { + try { + sqlSearch.close(); + sqlSearch = null; + } catch (SQLException e) { + log(null, e); + } + } + } + + @Override + public void onAuth(String resource) { + log("XMPP AUTH: " + resource); + } + + @Override + public void onAuthFailed(String message) { + log("XMPP AUTH FAILED: " + message); + } + + @Override + public void onConnectionFailed(String message) { + log("XMPP CONNECTION FAILED: " + message); } /** @@ -97,7 +131,12 @@ public class Main extends HttpServlet { } else if (uri.equals("/map")) { map.doGet(sql, request, response); } else if (uri.equals("/post")) { - pagesNewMessage.doGetNewMessage(sql, request, response); + com.juick.User visitor = Utils.getVisitorUser(sql, request); + if (visitor != null) { + pagesNewMessage.doGetNewMessage(sql, request, response, visitor); + } else { + login.doGetLoginForm(sql, request, response); + } } else if (uri.equals("/login")) { if (request.getQueryString() == null) { login.doGetLoginForm(sql, request, response); diff --git a/src/java/com/juick/http/www/Map.java b/src/java/com/juick/http/www/Map.java index 1bc625b8..96524ac0 100644 --- a/src/java/com/juick/http/www/Map.java +++ b/src/java/com/juick/http/www/Map.java @@ -56,7 +56,7 @@ public class Map { out.println("$(window).unload(GUnload);"); out.println("</script>"); - PageTemplates.pageFooter(out, locale); + PageTemplates.pageFooter(request, out, locale, visitor); } finally { out.close(); } diff --git a/src/java/com/juick/http/www/NewMessage.java b/src/java/com/juick/http/www/NewMessage.java index 8a92b475..1beacf62 100644 --- a/src/java/com/juick/http/www/NewMessage.java +++ b/src/java/com/juick/http/www/NewMessage.java @@ -17,8 +17,16 @@ */ package com.juick.http.www; +import com.juick.Tag; +import com.juick.server.TagQueries; import java.io.IOException; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; import java.sql.Connection; +import java.util.ArrayList; +import java.util.Locale; +import java.util.ResourceBundle; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -29,7 +37,85 @@ import javax.servlet.http.HttpServletResponse; */ public class NewMessage { - protected void doGetNewMessage(Connection sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + protected void doGetNewMessage(Connection sql, HttpServletRequest request, HttpServletResponse response, com.juick.User visitor) throws ServletException, IOException { + Locale locale = request.getLocale(); + ResourceBundle rbnm = ResourceBundle.getBundle("NewMessage", locale); + + response.setContentType("text/html; charset=UTF-8"); + PrintWriter out = response.getWriter(); + try { + PageTemplates.pageHead(out, rbnm.getString("New message"), "<script src=\"http://maps.google.com/maps?file=api&v=2&sensor=false&key=ABQIAAAAVVtPtxkw4soCEHg44FsNChRB4OFYjAXt73He16Zkp6a_0tPs2RTU6i6UlcMs4QvPBYvIY8rWvcxqOg\" type=\"text/javascript\"></script>" + + "<script src=\"http://static.juick.com/mc.js\" type=\"text/javascript\"></script>" + + "<script src=\"http://static.juick.com/map.js?2010111500\" type=\"text/javascript\"></script>" + + "<script src=\"http://static.juick.com/post3.js\" type=\"text/javascript\"></script>"); + PageTemplates.pageNavigation(out, locale, visitor); + PageTemplates.pageTitle(out, rbnm.getString("New message")); + + out.println("<div id=\"wrapper\"><div id=\"content\" class=\"pagetext\">"); + out.println("<form action=\"/post\" method=\"post\" id=\"postmsg\" enctype=\"multipart/form-data\">"); + out.println("<p style=\"text-align: left\"><b>" + rbnm.getString("Location") + ": <span id=\"location\"></span></b> <span id=\"locationclear\">— <a href=\"#\" onclick=\"clearLocation()\">" + rbnm.getString("Clear") + "</a></span></p>"); + out.println("<p style=\"text-align: left\"><b>" + rbnm.getString("Attachment") + ":</b> <span id=\"attachmentfile\"><input type=\"file\" name=\"attach\"$canmedia/> " + rbnm.getString("or") + " <a href=\"#\" onclick=\"webcamShow(); return false;\">" + rbnm.getString("from webcam") + "</a><br/>"); + out.println("<i>" + rbnm.getString("Photo_JPG") + "</i></span><span id=\"attachmentwebcam\">" + rbnm.getString("Webcam photo") + " — <a href=\"#\" onclick=\"clearAttachment(); return false;\">" + rbnm.getString("Clear") + "</a></span></p>"); + out.println("<div id=\"webcamwrap\" style=\"width: 320px; margin: 0 auto\"><div id=\"webcam\"></div></div>"); + out.println("<p><textarea name=\"body\" rows=\"7\" cols=\"10\">" + "" + "</textarea><br/>"); + out.println("<input type=\"hidden\" name=\"place_id\"/><input type=\"hidden\" name=\"webcam\"/>" + "" + "<input type=\"submit\" class=\"subm\" value=\" " + rbnm.getString("Post") + " \"/></p>"); + out.println("</form>"); + out.println("<div id=\"geomap\"></div>"); + out.println("<p style=\"text-align: left\"><b>" + rbnm.getString("Tags") + ":</b></p>"); + printUserTags(sql, out, visitor.UID); + out.println("</div>"); + out.println("</div>"); + + PageTemplates.pageFooter(request, out, locale, visitor); + } finally { + out.close(); + } + } + + void printUserTags(Connection sql, PrintWriter out, int uid) { + ArrayList<Tag> tags = TagQueries.getUserTagsAll(sql, uid); + + if (tags.isEmpty()) { + return; + } + + int min = tags.get(0).UsageCnt; + int max = tags.get(0).UsageCnt; + for (int i = 1; i < tags.size(); i++) { + int usagecnt = tags.get(i).UsageCnt; + if (usagecnt < min) { + min = usagecnt; + } + if (usagecnt > max) { + max = usagecnt; + } + } + max -= min; + + out.print("<p style=\"text-align: justify\">"); + for (int i = 0; i < tags.size(); i++) { + if (i > 0) { + out.print(" "); + } + String taglink = ""; + try { + taglink = "<a onclick=\"return addTag('" + Utils.encodeHTML(tags.get(i).Name) + "')\" href=\"/?tag=" + URLEncoder.encode(tags.get(i).Name, "utf-8") + "\" title=\"" + tags.get(i).UsageCnt + "\">" + Utils.encodeHTML(tags.get(i).Name) + "</a>"; + } catch (UnsupportedEncodingException e) { + } + int usagecnt = tags.get(i).UsageCnt; + if (usagecnt <= max / 5 + min) { + out.print("<span style=\"font-size: small\">" + taglink + "</span>"); + } else if (usagecnt <= max / 5 * 2 + min) { + out.print(taglink); + } else if (usagecnt <= max / 5 * 3 + min) { + out.print("<span style=\"font-size: large\">" + taglink + "</span>"); + } else if (usagecnt <= max / 5 * 4 + min) { + out.print("<span style=\"font-size: x-large\">" + taglink + "</span>"); + } else { + out.print("<span style=\"font-size: xx-large\">" + taglink + "</span>"); + } + } + out.println("</p>"); } protected void doPostNewMessage(Connection sql, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { diff --git a/src/java/com/juick/http/www/PageTemplates.java b/src/java/com/juick/http/www/PageTemplates.java index 94561dfd..eb98a4f8 100644 --- a/src/java/com/juick/http/www/PageTemplates.java +++ b/src/java/com/juick/http/www/PageTemplates.java @@ -27,6 +27,8 @@ import java.sql.SQLException; import java.util.ArrayList; import java.util.Locale; import java.util.ResourceBundle; +import javax.servlet.http.HttpServletRequest; +import ru.sape.Sape; /** * @@ -34,6 +36,8 @@ import java.util.ResourceBundle; */ public class PageTemplates { + public static Sape sape = null; + public static void pageHead(PrintWriter out, String title, String headers) { out.println("<!DOCTYPE html>"); out.println("<html>"); @@ -61,7 +65,7 @@ public class PageTemplates { out.println("<div id=\"logo\"><a href=\"/?show=my\"><img src=\"http://static.juick.com/logo3.png\" width=\"120\" height=\"40\" alt=\"Juick\" border=\"0\"/></a></div>"); out.println(" <ul id=\"nav\">"); out.println(" <li><a href=\"/\">" + rb.getString("Blogs") + "</a></li>"); - out.println(" <li><a href=\"/chats\">" + rb.getString("Chats") + "</a></li>"); +// out.println(" <li><a href=\"/chats\">" + rb.getString("Chats") + "</a></li>"); out.println(" <li><a href=\"/photos\">" + rb.getString("Photos") + "</a></li>"); out.println(" <li><a href=\"/map\">" + rb.getString("Map") + "</a></li>"); out.println(" </ul>"); @@ -187,11 +191,18 @@ public class PageTemplates { out.println(); } - public static void pageFooter(PrintWriter out, Locale loc) { + public static void pageFooter(HttpServletRequest request, PrintWriter out, Locale loc, com.juick.User visitor) { ResourceBundle rb = ResourceBundle.getBundle("Global", loc); out.println("<div id=\"fwrapper\"><div id=\"footer\">"); out.println(" <div id=\"footer-right\"><a href=\"/help/contacts\">" + rb.getString("Contacts") + "</a> · <a href=\"/help/\">" + rb.getString("Help") + "</a></div>"); - out.println(" <div id=\"footer-left\">juick.com © 2008-2011</div>"); + out.print(" <div id=\"footer-left\">juick.com © 2008-2012"); + if (sape != null && visitor == null) { + String links = sape.getPageLinks(request.getRequestURI(), request.getCookies()).render(); + if (links != null && !links.isEmpty()) { + out.print("<br/>" + rb.getString("Sponsored by") + ": " + links); + } + } + out.println("</div>"); out.println("</div>"); } @@ -331,15 +342,15 @@ public class PageTemplates { txt = formatMessage(txt); + out.print(" <li id=\"msg-" + mid + "\" class=\"msg\""); if (mid == mids.get(0)) { - out.println(" <li class=\"msg\" style=\"border: 0\">"); - } else { - out.println(" <li class=\"msg\">"); + out.print(" style=\"border: 0\""); } + out.println(">"); if (rs.getString(12) != null) { if (rs.getString(12).equals("jpg")) { - out.println(" <div class=\"msg-media\"><img src=\"http://i.juick.com/photos-512/" + mid + ".jpg\" alt=\"\"/></div>"); + out.println(" <div class=\"msg-media\"><a href=\"http://i.juick.com/photos-1024/" + mid + ".jpg\"><img src=\"http://i.juick.com/photos-512/" + mid + ".jpg\" alt=\"\"/></a></div>"); } else { out.println(" <div class=\"msg-media\"><div id=\"video-" + mid + "\"><b>Attachment: <a href=\"http://i.juick.com/video/" + mid + ".mp4\">Video</a></b></div></div>"); out.println(" <script type=\"text/javascript\">"); diff --git a/src/java/com/juick/http/www/Photos.java b/src/java/com/juick/http/www/Photos.java index 7a202625..a2665501 100644 --- a/src/java/com/juick/http/www/Photos.java +++ b/src/java/com/juick/http/www/Photos.java @@ -92,7 +92,7 @@ public class Photos { out.println("});"); out.println("</script>"); - PageTemplates.pageFooter(out, locale); + PageTemplates.pageFooter(request, out, locale, visitor); } finally { out.close(); } diff --git a/src/java/com/juick/http/www/User.java b/src/java/com/juick/http/www/User.java index fefe1174..9d692a44 100644 --- a/src/java/com/juick/http/www/User.java +++ b/src/java/com/juick/http/www/User.java @@ -140,7 +140,7 @@ public class User { out.println("</div>"); out.println("</div>"); - PageTemplates.pageFooter(out, locale); + PageTemplates.pageFooter(request, out, locale, visitor); } finally { out.close(); } diff --git a/src/java/com/juick/http/www/UserThread.java b/src/java/com/juick/http/www/UserThread.java index 6688e964..30787297 100644 --- a/src/java/com/juick/http/www/UserThread.java +++ b/src/java/com/juick/http/www/UserThread.java @@ -40,7 +40,6 @@ public class UserThread { protected void doGetThread(Connection sql, HttpServletRequest request, HttpServletResponse response, com.juick.User user, int MID) throws ServletException, IOException { com.juick.User visitor = Utils.getVisitorUser(sql, request); Locale locale = request.getLocale(); - ResourceBundle rb = ResourceBundle.getBundle("User", locale); boolean listview = false; String paramView = request.getParameter("view"); @@ -69,24 +68,9 @@ public class UserThread { out.println("<div id=\"wrapper\">"); out.println("<div id=\"content\" style=\"margin-left: 0; width: 100%\">"); - out.println("<ul>"); printMessage(out, sql, MID, locale); - out.println("</ul>"); - - out.println("<div class=\"title2\">"); - out.print(" <div class=\"title2-right\">"); - if (listview) { - out.print("<a href=\"?view=tree\">" + rb.getString("View as tree") + "</a>"); - } else { - out.print("<a href=\"#\" onclick=\"$('#replies>li').show(); $('#replies .msg-comments').hide(); return false\">" + rb.getString("Expand all") + "</a> · <a href=\"?view=list\">" + rb.getString("View as list") + "</a>"); - } - out.print("</div>"); - out.println(" <h2>Replies</h2>"); - out.println("</div>"); - out.println("<ul id=\"replies\">"); printReplies(out, sql, MID, locale, listview); - out.println("</ul>"); out.println("<script type=\"text/javascript\">"); out.println("$(\"textarea\").autoResize();"); @@ -96,7 +80,7 @@ public class UserThread { out.println("</div>"); - PageTemplates.pageFooter(out, locale); + PageTemplates.pageFooter(request, out, locale, visitor); } finally { out.close(); } @@ -143,11 +127,12 @@ public class UserThread { txt = PageTemplates.formatMessage(txt); - out.println(" <li class=\"msg\" style=\"border: 0\">"); + out.println("<ul>"); + out.println(" <li id=\"msg-" + mid + "\" class=\"msg\" style=\"border: 0\">"); if (rs.getString(11) != null) { if (rs.getString(11).equals("jpg")) { - out.println(" <div class=\"msg-media\"><img src=\"http://i.juick.com/photos-512/" + mid + ".jpg\" alt=\"\"/></div>"); + out.println(" <div class=\"msg-media\"><a href=\"http://i.juick.com/photos-1024/" + mid + ".jpg\"><img src=\"http://i.juick.com/photos-512/" + mid + ".jpg\" alt=\"\"/></a></div>"); } else { out.println(" <div class=\"msg-media\"><div id=\"video-" + mid + "\"><b>Attachment: <a href=\"http://i.juick.com/video/" + mid + ".mp4\">Video</a></b></div></div>"); out.println(" <script type=\"text/javascript\">"); @@ -162,7 +147,13 @@ public class UserThread { out.println(" </ul></div></div>"); out.println(" <div class=\"msg-header\"><a href=\"/" + uname + "/\">@" + uname + "</a>:" + tags + "</div>"); out.println(" <div class=\"msg-txt\">" + txt + "</div>"); + + out.println(" <form action=\"/post\" method=\"POST\" enctype=\"multipart/form-data\"><input type=\"hidden\" name=\"mid\" value=\"" + mid + "\"/>"); + out.println(" <div class=\"msg-comment\"><textarea name=\"body\" rows=\"1\" placeholder=\"Add a comment...\" onkeypress=\"postformListener(this.form,event)\"></textarea></div>"); + out.println(" </form>"); + out.println(" </li>"); + out.println("</ul>"); } } catch (SQLException e) { System.err.println(e); @@ -173,6 +164,7 @@ public class UserThread { } public static void printReplies(PrintWriter out, Connection sql, int mid, Locale locale, boolean listview) { + ResourceBundle rbuser = ResourceBundle.getBundle("User", locale); ArrayList<com.juick.Message> replies = new ArrayList<com.juick.Message>(); PreparedStatement stmt = null; @@ -217,10 +209,26 @@ public class UserThread { Utils.finishSQL(rs, stmt); } - if (listview) { - printList(out, replies, locale); - } else { - printTree(out, replies, 0, 0, locale); + if (!replies.isEmpty()) { + + out.println("<div class=\"title2\">"); + out.print(" <div class=\"title2-right\">"); + if (listview) { + out.print("<a href=\"?view=tree\">" + rbuser.getString("View as tree") + "</a>"); + } else { + out.print("<a href=\"#\" onclick=\"$('#replies>li').show(); $('#replies .msg-comments').hide(); return false\">" + rbuser.getString("Expand all") + "</a> · <a href=\"?view=list\">" + rbuser.getString("View as list") + "</a>"); + } + out.print("</div>"); + out.println(" <h2>Replies (" + replies.size() + ")</h2>"); + out.println("</div>"); + + out.println("<ul id=\"replies\">"); + if (listview) { + printList(out, replies, locale); + } else { + printTree(out, replies, 0, 0, locale); + } + out.println("</ul>"); } for (int i = 0; i < replies.size(); i++) { @@ -230,6 +238,8 @@ public class UserThread { } public static void printTree(PrintWriter out, ArrayList<com.juick.Message> replies, int ReplyTo, int margin, Locale locale) { + ResourceBundle rb = ResourceBundle.getBundle("Global", locale); + for (int i = 0; i < replies.size(); i++) { com.juick.Message msg = replies.get(i); if (msg.ReplyTo == ReplyTo) { @@ -258,6 +268,8 @@ public class UserThread { out.println(" </ul></div></div>"); out.println(" <div class=\"msg-header\"><a href=\"/" + msg.User.UName + "/\">@" + msg.User.UName + "</a>:</div>"); out.println(" <div class=\"msg-txt\">" + msg.Text + "</div>"); + out.println(" <div class=\"msg-links\"><a href=\"#\" onclick=\"return showCommentFormComment(" + msg.MID + "," + msg.RID + ")\">" + rb.getString("Comment") + "</a></div>"); + out.println(" <div class=\"msg-comment\" style=\"display: none\"></div>"); if (ReplyTo == 0) { int childs = msg.getChildsCount() - 1; if (childs > 0) { @@ -272,6 +284,8 @@ public class UserThread { } public static void printList(PrintWriter out, ArrayList<com.juick.Message> replies, Locale locale) { + ResourceBundle rb = ResourceBundle.getBundle("Global", locale); + for (int i = 0; i < replies.size(); i++) { com.juick.Message msg = replies.get(i); @@ -292,6 +306,8 @@ public class UserThread { out.println(" </ul></div></div>"); out.println(" <div class=\"msg-header\"><a href=\"/" + msg.User.UName + "/\">@" + msg.User.UName + "</a>:</div>"); out.println(" <div class=\"msg-txt\">" + msg.Text + "</div>"); + out.println(" <div class=\"msg-links\"><a href=\"#\" onclick=\"return showCommentFormComment(" + msg.MID + "," + msg.RID + ")\">" + rb.getString("Comment") + "</a></div>"); + out.println(" <div class=\"msg-comment\" style=\"display: none\"></div>"); out.println(" </li>"); } } diff --git a/src/java/com/juick/http/www/Utils.java b/src/java/com/juick/http/www/Utils.java index cfa4484d..80577d04 100644 --- a/src/java/com/juick/http/www/Utils.java +++ b/src/java/com/juick/http/www/Utils.java @@ -101,6 +101,8 @@ public class Utils { String ret = str; ret = ret.replaceAll("<", "<"); ret = ret.replaceAll(">", ">"); + ret = ret.replaceAll("'", "'"); + ret = ret.replaceAll("\"", """); return str; } diff --git a/src/java/ru/sape/Sape.java b/src/java/ru/sape/Sape.java new file mode 100644 index 00000000..c00054ae --- /dev/null +++ b/src/java/ru/sape/Sape.java @@ -0,0 +1,25 @@ +/* + * http://code.google.com/p/javasape/ + */ +package ru.sape; + +import javax.servlet.http.Cookie; + +public class Sape { + + private final String sapeUser; + private final SapeConnection sapePageLinkConnection; + + public Sape(String sapeUser, String host, int socketTimeout, int cacheLifeTime) { + this.sapeUser = sapeUser; + + this.sapePageLinkConnection = new SapeConnection( + "/code.php?user=" + sapeUser + "&host=" + host, + "SAPE_Client PHP", socketTimeout, cacheLifeTime); + } + public boolean debug = false; + + public SapePageLinks getPageLinks(String requestUri, Cookie[] cookies) { + return new SapePageLinks(sapePageLinkConnection, sapeUser, requestUri, cookies, debug); + } +} diff --git a/src/java/ru/sape/SapeConnection.java b/src/java/ru/sape/SapeConnection.java new file mode 100644 index 00000000..8c794b08 --- /dev/null +++ b/src/java/ru/sape/SapeConnection.java @@ -0,0 +1,107 @@ +package ru.sape; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.io.StringWriter; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class SapeConnection { + + private final String version = "1.0.3"; + private final List<String> serverList = Arrays.asList("dispenser-01.sape.ru", "dispenser-02.sape.ru"); + private final String dispenserPath; + private final String userAgent; + private final int socketTimeout; + private final int cacheLifeTime; + + public SapeConnection(String dispenserPath, String userAgent, int socketTimeout, int cacheLifeTime) { + this.dispenserPath = dispenserPath; + this.userAgent = userAgent; + this.socketTimeout = socketTimeout; + this.cacheLifeTime = cacheLifeTime; + } + + protected String fetchRemoteFile(String host, String path) throws IOException { + Reader r = null; + + try { + HttpURLConnection connection = (HttpURLConnection) ((new URL(("http://" + host + path)).openConnection())); + + if (socketTimeout > 0) { + connection.setConnectTimeout(socketTimeout); + connection.setReadTimeout(socketTimeout); + } + + connection.addRequestProperty("User-Agent", userAgent + ' ' + version); + + connection.setDoOutput(true); + connection.setDoInput(true); + connection.setUseCaches(false); + connection.setRequestMethod("GET"); + connection.connect(); + + r = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8")); + + StringWriter sw = new StringWriter(); + + int b; + + while ((b = r.read()) != -1) { + sw.write(b); + } + + return sw.toString(); + } finally { + if (r != null) { + r.close(); + } + } + } + Map<String, Object> cached; + long cacheUpdated; + + @SuppressWarnings("unchecked") + public Map<String, Object> getData() { + if (cacheLifeTime <= (System.currentTimeMillis() - cacheUpdated) / 1000) { + for (String server : serverList) { + String data; + + try { + data = fetchRemoteFile(server, dispenserPath + "&charset=UTF-8"); + } catch (IOException e1) { + continue; + } + + if (data.startsWith("FATAL ERROR:")) { + System.err.println("Sape responded with error: " + data); + + continue; + } + + try { + cached = (Map<String, Object>) new SerializedPhpParser(data).parse(); + } catch (Exception e) { + System.err.println("Can't parse Sape data: " + e); + continue; + } + + cacheUpdated = System.currentTimeMillis(); + + return cached; + } + + System.err.println("Unable to fetch Sape data"); + + return new HashMap<String, Object>(); + } + + return cached; + } +} diff --git a/src/java/ru/sape/SapePageLinks.java b/src/java/ru/sape/SapePageLinks.java new file mode 100644 index 00000000..498aeac0 --- /dev/null +++ b/src/java/ru/sape/SapePageLinks.java @@ -0,0 +1,95 @@ +package ru.sape; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import javax.servlet.http.Cookie; + +public class SapePageLinks { + + private boolean showCode; + + public SapePageLinks(SapeConnection sapeConnection, String sapeUser, String requestUri, Cookie[] cookies) { + this(sapeConnection, sapeUser, requestUri, cookies, false); + } + + @SuppressWarnings("unchecked") + public SapePageLinks(SapeConnection sapeConnection, String sapeUser, String requestUri, Cookie[] cookies, boolean showCode) { + if (sapeUser.equals(getCookieValue(cookies, "sape_cookie"))) { + showCode = true; + } + + Map<String, Object> data = sapeConnection.getData(); + + if (data.containsKey("__sape_delimiter__")) { + linkDelimiter = (String) data.get("__sape_delimiter__"); + } + + if (data.containsKey(requestUri)) { + pageLinks = new ArrayList<String>(((Map<Object, String>) data.get(requestUri)).values()); + } + + if (data.containsKey("__sape_new_url__")) { + if (showCode) { + Object newUrl = data.get("__sape_new_url__"); + + if (newUrl instanceof Map) { + pageLinks = new ArrayList<String>(((Map<Object, String>) newUrl).values()); + } else { + pageLinks = new ArrayList<String>(Arrays.asList((String) newUrl)); + } + } + } + + this.showCode = showCode; + } + private String linkDelimiter = "."; + private List<String> pageLinks = new ArrayList<String>(); + + public String render() { + return render(-1); + } + + public String render(int count) { + StringBuilder s = new StringBuilder(); + + if (count < 0) { + count = pageLinks.size(); + } + + for (Iterator<String> i = pageLinks.iterator(); i.hasNext() && count > 0; count--) { + if (s.length() > 0) { + s.append(linkDelimiter); + } + + String l = i.next(); + + s.append(l); + + i.remove(); + } + + if (showCode) { + s.insert(0, "<sape_noindex>"); + s.append("</sape_noindex>"); + } + + return s.toString(); + } + + private static String getCookieValue(Cookie[] cookies, String name) { + if (cookies == null) { + return null; + } + + for (Cookie cookie : cookies) { + if (cookie.getName().equals(name)) { + return cookie.getValue(); + } + } + + return null; + } +} diff --git a/src/java/ru/sape/SerializedPhpParser.java b/src/java/ru/sape/SerializedPhpParser.java new file mode 100644 index 00000000..a24551b9 --- /dev/null +++ b/src/java/ru/sape/SerializedPhpParser.java @@ -0,0 +1,221 @@ +/* +Copyright (c) 2007 Zsolt Szász <zsolt at lorecraft dot com> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package ru.sape; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.regex.Pattern; + +/** + * Deserializes a serialized PHP data structure into corresponding Java objects. It supports + * the integer, float, boolean, string primitives that are mapped to their Java + * equivalent, plus arrays that are parsed into <code>Map</code> instances and objects + * that are represented by {@link SerializedPhpParser.PhpObject} instances. + * <p> + * Example of use: + * <pre> + * String input = "O:8:"TypeName":1:{s:3:"foo";s:3:"bar";}"; + * SerializedPhpParser serializedPhpParser = new SerializedPhpParser(input); + * Object result = serializedPhpParser.parse(); + * </pre> + * + * The <code>result</code> object will be a <code>PhpObject</code> with the name "TypeName" and + * the attribute "foo" = "bar". + */ +class SerializedPhpParser { + + private final String input; + private int index; + private boolean assumeUTF8 = true; + private Pattern acceptedAttributeNameRegex = null; + + public SerializedPhpParser(String input) { + this.input = input; + } + + public Object parse() { + char type = input.charAt(index); + switch (type) { + case 'i': + index += 2; + return parseInt(); + case 'd': + index += 2; + return parseFloat(); + case 'b': + index += 2; + return parseBoolean(); + case 's': + index += 2; + return parseString(); + case 'a': + index += 2; + return parseArray(); + case 'O': + index += 2; + return parseObject(); + case 'N': + index += 2; + return NULL; + default: + throw new IllegalStateException("Encountered unknown type [" + type + "], str=" + input.substring(index)); + } + } + + private Object parseObject() { + PhpObject phpObject = new PhpObject(); + int strLen = readLength(); + phpObject.name = input.substring(index, index + strLen); + index = index + strLen + 2; + int attrLen = readLength(); + for (int i = 0; i < attrLen; i++) { + Object key = parse(); + Object value = parse(); + if (isAcceptedAttribute(key)) { + phpObject.attributes.put(key, value); + } + } + index++; + return phpObject; + } + + private Map<Object, Object> parseArray() { + int arrayLen = readLength(); + Map<Object, Object> result = new LinkedHashMap<Object, Object>(); + for (int i = 0; i < arrayLen; i++) { + Object key = parse(); + Object value = parse(); + if (isAcceptedAttribute(key)) { + result.put(key, value); + } + } + index++; + return result; + } + + private boolean isAcceptedAttribute(Object key) { + if (acceptedAttributeNameRegex == null) { + return true; + } + if (!(key instanceof String)) { + return true; + } + return acceptedAttributeNameRegex.matcher((String) key).matches(); + } + + private int readLength() { + int delimiter = input.indexOf(':', index); + int arrayLen = Integer.valueOf(input.substring(index, delimiter)); + index = delimiter + 2; + return arrayLen; + } + + /** + * Assumes strings are utf8 encoded + * + * @return + */ + private String parseString() { + int strLen = readLength(); + + int utfStrLen = 0; + int byteCount = 0; + while (byteCount != strLen) { + char ch = input.charAt(index + utfStrLen++); + + /* + if (ch == '\'') { + utfStrLen -= 1; + break; + } + */ + + if (assumeUTF8) { + if ((ch >= 0x0001) && (ch <= 0x007F)) { + byteCount++; + } else if (ch > 0x07FF) { + byteCount += 3; + } else { + byteCount += 2; + } + } else { + byteCount++; + } + } + String value = input.substring(index, index + utfStrLen); + index = index + utfStrLen + 2; + return value; + } + + private Boolean parseBoolean() { + int delimiter = input.indexOf(';', index); + String value = input.substring(index, delimiter); + if (value.equals("1")) { + value = "true"; + } else if (value.equals("0")) { + value = "false"; + } + index = delimiter + 1; + return Boolean.valueOf(value); + } + + private Double parseFloat() { + int delimiter = input.indexOf(';', index); + String value = input.substring(index, delimiter); + index = delimiter + 1; + return Double.valueOf(value); + } + + private Integer parseInt() { + int delimiter = input.indexOf(';', index); + String value = input.substring(index, delimiter); + index = delimiter + 1; + return Integer.valueOf(value); + } + + public void setAcceptedAttributeNameRegex(String acceptedAttributeNameRegex) { + this.acceptedAttributeNameRegex = Pattern.compile(acceptedAttributeNameRegex); + } + public static final Object NULL = new Object() { + + @Override + public String toString() { + return "NULL"; + } + }; + + /** + * Represents an object that has a name and a map of attributes + */ + public static class PhpObject { + + public String name; + public Map<Object, Object> attributes = new HashMap<Object, Object>(); + + @Override + public String toString() { + return "\"" + name + "\" : " + attributes.toString(); + } + } +} |