From bda2d764a9c187da5aa8cd74fb8c4a9574d3996e Mon Sep 17 00:00:00 2001 From: Vitaly Takmazov Date: Fri, 30 Mar 2018 17:52:53 +0300 Subject: xmpp integration tests --- juick-www/src/main/java/com/juick/Application.java | 3 + .../com/juick/configuration/SapeConfiguration.java | 37 -- .../com/juick/configuration/WebSecurityConfig.java | 140 ----- .../juick/configuration/WwwAppConfiguration.java | 123 ----- .../com/juick/configuration/XMPPConfiguration.java | 43 -- .../juick/www/configuration/SapeConfiguration.java | 37 ++ .../juick/www/configuration/WebSecurityConfig.java | 140 +++++ .../www/configuration/WwwAppConfiguration.java | 123 +++++ .../juick/www/configuration/XMPPConfiguration.java | 43 ++ .../java/com/juick/www/controllers/Messages.java | 576 --------------------- .../com/juick/www/controllers/MessagesWWW.java | 576 +++++++++++++++++++++ .../java/com/juick/www/controllers/NewMessage.java | 13 +- 12 files changed, 927 insertions(+), 927 deletions(-) delete mode 100644 juick-www/src/main/java/com/juick/configuration/SapeConfiguration.java delete mode 100644 juick-www/src/main/java/com/juick/configuration/WebSecurityConfig.java delete mode 100644 juick-www/src/main/java/com/juick/configuration/WwwAppConfiguration.java delete mode 100644 juick-www/src/main/java/com/juick/configuration/XMPPConfiguration.java create mode 100644 juick-www/src/main/java/com/juick/www/configuration/SapeConfiguration.java create mode 100644 juick-www/src/main/java/com/juick/www/configuration/WebSecurityConfig.java create mode 100644 juick-www/src/main/java/com/juick/www/configuration/WwwAppConfiguration.java create mode 100644 juick-www/src/main/java/com/juick/www/configuration/XMPPConfiguration.java delete mode 100644 juick-www/src/main/java/com/juick/www/controllers/Messages.java create mode 100644 juick-www/src/main/java/com/juick/www/controllers/MessagesWWW.java (limited to 'juick-www/src/main') diff --git a/juick-www/src/main/java/com/juick/Application.java b/juick-www/src/main/java/com/juick/Application.java index f8e5d333..a7a7a654 100644 --- a/juick-www/src/main/java/com/juick/Application.java +++ b/juick-www/src/main/java/com/juick/Application.java @@ -4,10 +4,13 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Primary; import org.springframework.transaction.annotation.EnableTransactionManagement; @SpringBootApplication @EnableTransactionManagement +@ComponentScan(basePackages = {"com.juick.www", "com.juick.service"}) public class Application extends SpringBootServletInitializer { @Override diff --git a/juick-www/src/main/java/com/juick/configuration/SapeConfiguration.java b/juick-www/src/main/java/com/juick/configuration/SapeConfiguration.java deleted file mode 100644 index 2630c05b..00000000 --- a/juick-www/src/main/java/com/juick/configuration/SapeConfiguration.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.configuration; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import ru.sape.Sape; - -/** - * Created by vitalyster on 29.03.2017. - */ -@Configuration -public class SapeConfiguration { - @Value("${sape_user:secret}") - private String token; - - @Bean - public Sape sape() { - return new Sape(token, "juick.com", 2000, 3600); - } -} diff --git a/juick-www/src/main/java/com/juick/configuration/WebSecurityConfig.java b/juick-www/src/main/java/com/juick/configuration/WebSecurityConfig.java deleted file mode 100644 index 92f43e64..00000000 --- a/juick-www/src/main/java/com/juick/configuration/WebSecurityConfig.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.configuration; - -import com.juick.service.UserService; -import com.juick.service.security.HashParamAuthenticationFilter; -import com.juick.service.security.JuickUserDetailsService; -import com.juick.service.security.entities.JuickUser; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.dao.DaoAuthenticationProvider; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.builders.WebSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.web.authentication.RememberMeServices; -import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices; -import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; - -import javax.annotation.Resource; - -/** - * Created by aalexeev on 11/21/16. - */ -@EnableWebSecurity -public class WebSecurityConfig extends WebSecurityConfigurerAdapter { - @Value("${auth_remember_me_key:secret}") - private String rememberMeKey; - @Value("${web_domain:localhost}") - private String webDomain; - @Resource - private UserService userService; - - private final String COOKIE_NAME = "juick-remember-me"; - - @Bean("userDetailsService") - @Override - public UserDetailsService userDetailsServiceBean() throws Exception { - return super.userDetailsServiceBean(); - } - - @Override - public UserDetailsService userDetailsService() { - return new JuickUserDetailsService(userService); - } - - @Bean("authenticationManager") - @Override - public AuthenticationManager authenticationManagerBean() throws Exception { - return super.authenticationManagerBean(); - } - - @Override - protected void configure(HttpSecurity http) throws Exception { - http.addFilterAfter(hashParamAuthenticationFilter(), BasicAuthenticationFilter.class); - http - .authorizeRequests() - .antMatchers("/settings", "/pm/**", "/**/bl", "/_twitter", "/post", "/comment") - .authenticated() - .anyRequest().permitAll() - .and() - .anonymous().principal(JuickUser.ANONYMOUS_USER).authorities(JuickUser.ANONYMOUS_AUTHORITY) - .and() - .sessionManagement().invalidSessionUrl("/") - .and() - .logout() - .invalidateHttpSession(true) - .logoutUrl("/logout") - .logoutSuccessUrl("/login?logout") - .deleteCookies("hash", COOKIE_NAME) - .and() - .formLogin() - .loginPage("/login") - .permitAll() - .defaultSuccessUrl("/") - .loginProcessingUrl("/login") - .usernameParameter("username") - .passwordParameter("password") - .failureUrl("/login?error=1") - .and() - .rememberMe() - .rememberMeCookieDomain(webDomain).key(rememberMeKey) - .rememberMeServices(rememberMeServices()) - .and() - .csrf().disable() - .authenticationProvider(authenticationProvider()) - .headers().defaultsDisabled().cacheControl(); - } - - @Bean - public DaoAuthenticationProvider authenticationProvider() throws Exception { - DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); - - authenticationProvider.setUserDetailsService(userDetailsService()); - - return authenticationProvider; - } - - @Bean - public HashParamAuthenticationFilter hashParamAuthenticationFilter() throws Exception { - return new HashParamAuthenticationFilter(userService, rememberMeServices()); - } - - @Bean - public RememberMeServices rememberMeServices() throws Exception { - TokenBasedRememberMeServices services = new TokenBasedRememberMeServices( - rememberMeKey, userDetailsService()); - - services.setCookieName(COOKIE_NAME); - services.setCookieDomain(webDomain); - services.setAlwaysRemember(true); - services.setTokenValiditySeconds(6 * 30 * 24 * 3600); - services.setUseSecureCookie(false); // TODO set true if https is supports - - return services; - } - - @Override - public void configure(WebSecurity web) throws Exception { - web.debug(false); - web.ignoring().antMatchers("/style.css*", "/scripts.js*"); - } -} diff --git a/juick-www/src/main/java/com/juick/configuration/WwwAppConfiguration.java b/juick-www/src/main/java/com/juick/configuration/WwwAppConfiguration.java deleted file mode 100644 index ea585a6f..00000000 --- a/juick-www/src/main/java/com/juick/configuration/WwwAppConfiguration.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ - -package com.juick.configuration; - -import com.juick.server.configuration.BaseWebConfiguration; -import com.juick.server.configuration.StorageConfiguration; -import com.juick.service.CloudflareCache; -import com.juick.service.TagService; -import com.juick.service.UserService; -import com.juick.www.HelpService; -import com.mitchellbosecke.pebble.PebbleEngine; -import com.mitchellbosecke.pebble.extension.FormatterExtension; -import com.mitchellbosecke.pebble.loader.ClasspathLoader; -import com.mitchellbosecke.pebble.loader.Loader; -import com.mitchellbosecke.pebble.spring4.PebbleViewResolver; -import com.mitchellbosecke.pebble.spring4.extension.SpringExtension; -import org.apache.commons.codec.CharEncoding; -import org.commonmark.ext.autolink.AutolinkExtension; -import org.commonmark.node.Link; -import org.commonmark.parser.Parser; -import org.commonmark.renderer.html.HtmlRenderer; -import org.springframework.cache.annotation.EnableCaching; -import org.springframework.cache.caffeine.CaffeineCacheManager; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.web.servlet.ViewResolver; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -import javax.inject.Inject; -import java.util.Collections; - -/** - * Created by aalexeev on 11/22/16. - */ -@Configuration -@EnableCaching -@Import({ BaseWebConfiguration.class, WebSecurityConfig.class, SapeConfiguration.class, - StorageConfiguration.class, XMPPConfiguration.class}) -public class WwwAppConfiguration implements WebMvcConfigurer { - @Inject - private UserService userService; - @Inject - private TagService tagService; - @Bean - public CaffeineCacheManager cacheManager() { - return new CaffeineCacheManager("help"); - } - - @Bean - public HelpService helpService() { - return new HelpService("help"); - } - - @Bean - public Parser cmParser() { - return Parser.builder().extensions(Collections.singletonList(AutolinkExtension.create())).build(); - } - @Bean - public HtmlRenderer helpRenderer() { - return HtmlRenderer.builder() - .attributeProviderFactory(context -> (node, tagName, attributes) -> { - if (node instanceof Link) { - Link link = (Link) node; - if (link.getDestination().startsWith("/")) { - String destination = "/" + helpService().getHelpPath() + link.getDestination(); - link.setDestination(destination); - attributes.put("href", destination); - } - } - }) - .build(); - } - @Bean - public CloudflareCache cloudflareCache() { - return new CloudflareCache(); - } - @Bean - public Loader templateLoader() { - return new ClasspathLoader(); - } - - @Bean - public SpringExtension springExtension() { - return new SpringExtension(); - } - - @Bean - public PebbleEngine pebbleEngine() { - return new PebbleEngine.Builder() - .loader(this.templateLoader()) - .extension(springExtension()) - .extension(new FormatterExtension()) - .strictVariables(true) - .build(); - } - - @Bean - public ViewResolver viewResolver() { - PebbleViewResolver viewResolver = new PebbleViewResolver(); - viewResolver.setPrefix("templates"); - viewResolver.setSuffix(".html"); - viewResolver.setPebbleEngine(pebbleEngine()); - viewResolver.setCharacterEncoding(CharEncoding.UTF_8); - return viewResolver; - } - -} diff --git a/juick-www/src/main/java/com/juick/configuration/XMPPConfiguration.java b/juick-www/src/main/java/com/juick/configuration/XMPPConfiguration.java deleted file mode 100644 index 91f55759..00000000 --- a/juick-www/src/main/java/com/juick/configuration/XMPPConfiguration.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.juick.configuration; - -import com.juick.Message; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import rocks.xmpp.core.XmppException; -import rocks.xmpp.core.session.Extension; -import rocks.xmpp.core.session.XmppSessionConfiguration; -import rocks.xmpp.core.session.debug.LogbackDebugger; -import rocks.xmpp.extensions.component.accept.ExternalComponent; - -@Configuration -public class XMPPConfiguration { - private static Logger logger = LoggerFactory.getLogger(XMPPConfiguration.class); - @Value("${xmpp_host:localhost}") - private String xmppHost; - @Value("${xmpp_password:secret}") - private String xmppPassword; - @Value("${www_xmpp_jid:www.juick.local}") - private String xmppJid; - @Value("${xmpp_port:5347}") - private int xmppPort; - @Value("${xmpp_disabled:false}") - private boolean isXmppDisabled; - @Bean - public ExternalComponent xmpp() { - XmppSessionConfiguration configuration = XmppSessionConfiguration.builder() - .extensions(Extension.of(Message.class)) - .debugger(LogbackDebugger.class) - .build(); - ExternalComponent xmpp = ExternalComponent.create(xmppJid, xmppPassword, configuration, xmppHost, xmppPort); - xmpp.addConnectionListener(e -> logger.error(e.toString(), e.getCause())); - if (!isXmppDisabled) try { - xmpp.connect(); - } catch (XmppException e) { - logger.error("xmpp extension", e); - } - return xmpp; - } -} diff --git a/juick-www/src/main/java/com/juick/www/configuration/SapeConfiguration.java b/juick-www/src/main/java/com/juick/www/configuration/SapeConfiguration.java new file mode 100644 index 00000000..68ff28d2 --- /dev/null +++ b/juick-www/src/main/java/com/juick/www/configuration/SapeConfiguration.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2008-2017, Juick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.juick.www.configuration; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import ru.sape.Sape; + +/** + * Created by vitalyster on 29.03.2017. + */ +@Configuration +public class SapeConfiguration { + @Value("${sape_user:secret}") + private String token; + + @Bean + public Sape sape() { + return new Sape(token, "juick.com", 2000, 3600); + } +} diff --git a/juick-www/src/main/java/com/juick/www/configuration/WebSecurityConfig.java b/juick-www/src/main/java/com/juick/www/configuration/WebSecurityConfig.java new file mode 100644 index 00000000..909b6cd3 --- /dev/null +++ b/juick-www/src/main/java/com/juick/www/configuration/WebSecurityConfig.java @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2008-2017, Juick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.juick.www.configuration; + +import com.juick.service.UserService; +import com.juick.service.security.HashParamAuthenticationFilter; +import com.juick.service.security.JuickUserDetailsService; +import com.juick.service.security.entities.JuickUser; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.web.authentication.RememberMeServices; +import org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices; +import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; + +import javax.annotation.Resource; + +/** + * Created by aalexeev on 11/21/16. + */ +@EnableWebSecurity +public class WebSecurityConfig extends WebSecurityConfigurerAdapter { + @Value("${auth_remember_me_key:secret}") + private String rememberMeKey; + @Value("${web_domain:localhost}") + private String webDomain; + @Resource + private UserService userService; + + private final String COOKIE_NAME = "juick-remember-me"; + + @Bean("userDetailsService") + @Override + public UserDetailsService userDetailsServiceBean() throws Exception { + return super.userDetailsServiceBean(); + } + + @Override + public UserDetailsService userDetailsService() { + return new JuickUserDetailsService(userService); + } + + @Bean("authenticationManager") + @Override + public AuthenticationManager authenticationManagerBean() throws Exception { + return super.authenticationManagerBean(); + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.addFilterAfter(hashParamAuthenticationFilter(), BasicAuthenticationFilter.class); + http + .authorizeRequests() + .antMatchers("/settings", "/pm/**", "/**/bl", "/_twitter", "/post", "/post2", "/comment") + .authenticated() + .anyRequest().permitAll() + .and() + .anonymous().principal(JuickUser.ANONYMOUS_USER).authorities(JuickUser.ANONYMOUS_AUTHORITY) + .and() + .sessionManagement().invalidSessionUrl("/") + .and() + .logout() + .invalidateHttpSession(true) + .logoutUrl("/logout") + .logoutSuccessUrl("/login?logout") + .deleteCookies("hash", COOKIE_NAME) + .and() + .formLogin() + .loginPage("/login") + .permitAll() + .defaultSuccessUrl("/") + .loginProcessingUrl("/login") + .usernameParameter("username") + .passwordParameter("password") + .failureUrl("/login?error=1") + .and() + .rememberMe() + .rememberMeCookieDomain(webDomain).key(rememberMeKey) + .rememberMeServices(rememberMeServices()) + .and() + .csrf().disable() + .authenticationProvider(authenticationProvider()) + .headers().defaultsDisabled().cacheControl(); + } + + @Bean + public DaoAuthenticationProvider authenticationProvider() throws Exception { + DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider(); + + authenticationProvider.setUserDetailsService(userDetailsService()); + + return authenticationProvider; + } + + @Bean + public HashParamAuthenticationFilter hashParamAuthenticationFilter() throws Exception { + return new HashParamAuthenticationFilter(userService, rememberMeServices()); + } + + @Bean + public RememberMeServices rememberMeServices() throws Exception { + TokenBasedRememberMeServices services = new TokenBasedRememberMeServices( + rememberMeKey, userDetailsService()); + + services.setCookieName(COOKIE_NAME); + services.setCookieDomain(webDomain); + services.setAlwaysRemember(true); + services.setTokenValiditySeconds(6 * 30 * 24 * 3600); + services.setUseSecureCookie(false); // TODO set true if https is supports + + return services; + } + + @Override + public void configure(WebSecurity web) throws Exception { + web.debug(false); + web.ignoring().antMatchers("/style.css*", "/scripts.js*"); + } +} diff --git a/juick-www/src/main/java/com/juick/www/configuration/WwwAppConfiguration.java b/juick-www/src/main/java/com/juick/www/configuration/WwwAppConfiguration.java new file mode 100644 index 00000000..13a394cb --- /dev/null +++ b/juick-www/src/main/java/com/juick/www/configuration/WwwAppConfiguration.java @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2008-2017, Juick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package com.juick.www.configuration; + +import com.juick.server.configuration.BaseWebConfiguration; +import com.juick.server.configuration.StorageConfiguration; +import com.juick.service.CloudflareCache; +import com.juick.service.TagService; +import com.juick.service.UserService; +import com.juick.www.HelpService; +import com.mitchellbosecke.pebble.PebbleEngine; +import com.mitchellbosecke.pebble.extension.FormatterExtension; +import com.mitchellbosecke.pebble.loader.ClasspathLoader; +import com.mitchellbosecke.pebble.loader.Loader; +import com.mitchellbosecke.pebble.spring4.PebbleViewResolver; +import com.mitchellbosecke.pebble.spring4.extension.SpringExtension; +import org.apache.commons.codec.CharEncoding; +import org.commonmark.ext.autolink.AutolinkExtension; +import org.commonmark.node.Link; +import org.commonmark.parser.Parser; +import org.commonmark.renderer.html.HtmlRenderer; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.caffeine.CaffeineCacheManager; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.web.servlet.ViewResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import javax.inject.Inject; +import java.util.Collections; + +/** + * Created by aalexeev on 11/22/16. + */ +@Configuration +@EnableCaching +@Import({ BaseWebConfiguration.class, WebSecurityConfig.class, SapeConfiguration.class, + StorageConfiguration.class, XMPPConfiguration.class}) +public class WwwAppConfiguration implements WebMvcConfigurer { + @Inject + private UserService userService; + @Inject + private TagService tagService; + @Bean + public CaffeineCacheManager cacheManager() { + return new CaffeineCacheManager("help"); + } + + @Bean + public HelpService helpService() { + return new HelpService("help"); + } + + @Bean + public Parser cmParser() { + return Parser.builder().extensions(Collections.singletonList(AutolinkExtension.create())).build(); + } + @Bean + public HtmlRenderer helpRenderer() { + return HtmlRenderer.builder() + .attributeProviderFactory(context -> (node, tagName, attributes) -> { + if (node instanceof Link) { + Link link = (Link) node; + if (link.getDestination().startsWith("/")) { + String destination = "/" + helpService().getHelpPath() + link.getDestination(); + link.setDestination(destination); + attributes.put("href", destination); + } + } + }) + .build(); + } + @Bean + public CloudflareCache cloudflareCache() { + return new CloudflareCache(); + } + @Bean + public Loader templateLoader() { + return new ClasspathLoader(); + } + + @Bean + public SpringExtension springExtension() { + return new SpringExtension(); + } + + @Bean + public PebbleEngine pebbleEngine() { + return new PebbleEngine.Builder() + .loader(this.templateLoader()) + .extension(springExtension()) + .extension(new FormatterExtension()) + .strictVariables(true) + .build(); + } + + @Bean + public ViewResolver viewResolver() { + PebbleViewResolver viewResolver = new PebbleViewResolver(); + viewResolver.setPrefix("templates"); + viewResolver.setSuffix(".html"); + viewResolver.setPebbleEngine(pebbleEngine()); + viewResolver.setCharacterEncoding(CharEncoding.UTF_8); + return viewResolver; + } + +} diff --git a/juick-www/src/main/java/com/juick/www/configuration/XMPPConfiguration.java b/juick-www/src/main/java/com/juick/www/configuration/XMPPConfiguration.java new file mode 100644 index 00000000..1396f9f9 --- /dev/null +++ b/juick-www/src/main/java/com/juick/www/configuration/XMPPConfiguration.java @@ -0,0 +1,43 @@ +package com.juick.www.configuration; + +import com.juick.Message; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import rocks.xmpp.core.XmppException; +import rocks.xmpp.core.session.Extension; +import rocks.xmpp.core.session.XmppSessionConfiguration; +import rocks.xmpp.core.session.debug.LogbackDebugger; +import rocks.xmpp.extensions.component.accept.ExternalComponent; + +@Configuration +public class XMPPConfiguration { + private static Logger logger = LoggerFactory.getLogger(XMPPConfiguration.class); + @Value("${xmpp_host:localhost}") + private String xmppHost; + @Value("${xmpp_password:secret}") + private String xmppPassword; + @Value("${www_xmpp_jid:www.juick.local}") + private String xmppJid; + @Value("${xmpp_port:5347}") + private int xmppPort; + @Value("${xmpp_disabled:false}") + private boolean isXmppDisabled; + @Bean + public ExternalComponent xmpp() { + XmppSessionConfiguration configuration = XmppSessionConfiguration.builder() + .extensions(Extension.of(Message.class)) + .debugger(LogbackDebugger.class) + .build(); + ExternalComponent xmpp = ExternalComponent.create(xmppJid, xmppPassword, configuration, xmppHost, xmppPort); + xmpp.addConnectionListener(e -> logger.error(e.toString(), e.getCause())); + if (!isXmppDisabled) try { + xmpp.connect(); + } catch (XmppException e) { + logger.error("xmpp extension", e); + } + return xmpp; + } +} diff --git a/juick-www/src/main/java/com/juick/www/controllers/Messages.java b/juick-www/src/main/java/com/juick/www/controllers/Messages.java deleted file mode 100644 index 65e122a6..00000000 --- a/juick-www/src/main/java/com/juick/www/controllers/Messages.java +++ /dev/null @@ -1,576 +0,0 @@ -/* - * Copyright (C) 2008-2017, Juick - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see . - */ -package com.juick.www.controllers; - -import com.juick.Message; -import com.juick.Tag; -import com.juick.formatters.PlainTextFormatter; -import com.juick.server.util.HttpForbiddenException; -import com.juick.server.util.HttpNotFoundException; -import com.juick.server.util.UserUtils; -import com.juick.server.util.WebUtils; -import com.juick.service.*; -import com.juick.util.MessageUtils; -import com.juick.www.Utils; -import org.apache.commons.codec.CharEncoding; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.math.NumberUtils; -import org.apache.commons.text.StringEscapeUtils; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.ui.ModelMap; -import org.springframework.web.bind.annotation.*; -import org.springframework.web.servlet.support.ServletUriComponentsBuilder; -import org.springframework.web.util.UriComponents; -import ru.sape.Sape; - -import javax.inject.Inject; -import javax.servlet.http.HttpServletRequest; -import java.io.IOException; -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.List; -import java.util.stream.Collectors; - -/** - * - * @author Ugnich Anton - */ -@Controller -public class Messages { - @Inject - private UserService userService; - @Inject - private TagService tagService; - @Inject - private MessagesService messagesService; - @Inject - private Sape sape; - @Inject - private PMQueriesService pmQueriesService; - @Inject - private CrosspostService crosspostService; - - void fillUserModel(ModelMap model, com.juick.User user, com.juick.User visitor) { - model.addAttribute("user", user); - model.addAttribute("isSubscribed", userService.isSubscribed(visitor.getUid(), user.getUid())); - model.addAttribute("isInBL", userService.isInBL(visitor.getUid(), user.getUid())); - model.addAttribute("isInBLAny", userService.isInBLAny(user.getUid(), visitor.getUid())); - model.addAttribute("statsIRead", userService.getStatsIRead(user.getUid())); - model.addAttribute("statsMyReaders", userService.getStatsMyReaders(user.getUid())); - model.addAttribute("statsMyBL", userService.getUserBLUsers(user.getUid()).size()); - model.addAttribute("statsMessages", userService.getStatsMessages(user.getUid())); - model.addAttribute("statsReplies", userService.getStatsReplies(user.getUid())); - model.addAttribute("iread", userService.getUserReadLeastPopular(user.getUid(), 8)); - model.addAttribute("tagStats", tagService.getUserTagStats(user.getUid()) - .stream().sorted((e1, e2) -> Integer.compare(e2.getUsageCount(), e1.getUsageCount())).limit(20).map(t -> t.getTag().getName()).collect(Collectors.toList())); - } - - @GetMapping("/") - protected String doGet( - @RequestParam(required = false) String tag, - @RequestParam(name = "show", required = false) String paramShow, - @RequestParam(name = "search", required = false) String paramSearch, - @RequestParam(name = "before", required = false, defaultValue = "0") Integer paramBefore, - @RequestParam(name = "to", required = false, defaultValue = "0") Long paramTo, - @CookieValue(name = "sape_cookie", required = false, defaultValue = StringUtils.EMPTY) String sapeCookie, - ModelMap model) throws IOException { - if (tag != null) { - return "redirect:/tag/" + URLEncoder.encode(tag, CharEncoding.UTF_8); - } - com.juick.User visitor = UserUtils.getCurrentUser(); - - if (paramSearch != null && paramSearch.length() > 64) { - paramSearch = null; - } - - model.addAttribute("discover", false); - - String title; - List mids; - - if (paramSearch != null) { - title = "Поиск: " + StringEscapeUtils.escapeHtml4(paramSearch); - mids = messagesService.getSearch(Utils.encodeSphinx(paramSearch), paramBefore); - } else if (paramShow == null) { - if (visitor.getUid() > 0) { - title = "Популярные"; - mids = messagesService.getPopular(visitor.getUid(), paramBefore); - model.addAttribute("discover", true); - } else { - title = "Микроблоги Juick: популярные записи"; - mids = messagesService.getPopular(0, paramBefore); - } - - } else if (paramShow.equals("top")) { - return "redirect:/"; - } else if (paramShow.equals("my") && !visitor.isAnonymous()) { - title = "Моя лента"; - mids = messagesService.getMyFeed(visitor.getUid(), paramBefore, true); - } else if (paramShow.equals("private") && !visitor.isAnonymous()) { - title = "Приватные"; - mids = messagesService.getPrivate(visitor.getUid(), paramBefore); - } else if (paramShow.equals("discuss") && !visitor.isAnonymous()) { - title = "Обсуждения"; - mids = messagesService.getDiscussions(visitor.getUid(), paramTo); - } else if (paramShow.equals("recommended") && !visitor.isAnonymous()) { - title = "Рекомендации"; - mids = messagesService.getRecommended(visitor.getUid(), paramBefore); - } else if (paramShow.equals("photos")) { - title = "Фотографии"; - mids = messagesService.getPhotos(visitor.getUid(), paramBefore); - model.addAttribute("discover", true); - } else if (paramShow.equals("all")) { - title = "Все сообщения"; - mids = messagesService.getAll(visitor.getUid(), paramBefore); - model.addAttribute("discover", true); - } else { - throw new HttpNotFoundException(); - } - - String head = StringUtils.EMPTY; - if (paramBefore > 0 || paramShow != null) { - head = ""; - } - model.addAttribute("title", title); - model.addAttribute("headers", head); - model.addAttribute("visitor", visitor); - model.addAttribute("noindex", !(paramShow == null && paramBefore == 0)); - List msgs = messagesService.getMessages(mids); - - if (visitor.getUid() != 0) { - fillUserModel(model, visitor, visitor); - List blUIDs = userService.checkBL(visitor.getUid(), - msgs.stream().map(m -> m.getUser().getUid()).collect(Collectors.toList())); - msgs.forEach(m -> m.ReadOnly |= blUIDs.contains(m.getUser().getUid())); - } - model.addAttribute("msgs", msgs); - model.addAttribute("tags", tagService.getPopularTags()); - model.addAttribute("headers", head); - model.addAttribute("showAdv", - paramShow == null && paramBefore == 0 && paramSearch == null && visitor.getUid() == 0); - if (mids.size() >= 20) { - String nextpage = (paramShow != null && paramShow.equals("discuss")) ? "?to=" + msgs.get(msgs.size() - 1).getUpdated().toEpochMilli() : "?before=" + mids.get(mids.size() - 1); - if (paramShow != null) { - nextpage += "&show=" + paramShow; - } - if (paramSearch != null) { - nextpage += "&search=" + URLEncoder.encode(paramSearch, CharEncoding.UTF_8); - } - model.addAttribute("nextpage", nextpage); - } - UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequestUri().build(); - String queryString = builder.getQuery(); - String requestURI = builder.toUri().getPath(); - if (sape != null && visitor.getUid() == 0 && queryString == null) { - String links = sape.getPageLinks(requestURI, sapeCookie).render(); - model.addAttribute("links", links); - } - return "views/index"; - } - - @GetMapping("/{uname}/") - protected String doGetBlog( - @RequestParam(required = false, name = "show") String paramShow, - @RequestParam(required = false, name = "tag") String paramTagStr, - @RequestParam(required = false, name = "search") String paramSearch, - @PathVariable String uname, - @RequestParam(required = false, defaultValue = "0") Integer before, - @CookieValue(name = "sape_cookie", required = false, defaultValue = StringUtils.EMPTY) String sapeCookie, - ModelMap model) throws IOException { - com.juick.User user = userService.getUserByName(uname); - com.juick.User visitor = UserUtils.getCurrentUser(); - if (user.isBanned() || user.isAnonymous()) { - throw new HttpNotFoundException(); - } - - List mids; - - com.juick.Tag paramTag = null; - if (paramTagStr != null) { - if (paramTagStr.length() < 64) { - paramTag = tagService.getTag(paramTagStr, false); - } - if (paramTag == null) { - throw new HttpNotFoundException(); - } else if (!paramTag.getName().equals(paramTagStr)) { - String url = user.getName() + "/?tag=" + URLEncoder.encode(paramTag.getName(), CharEncoding.UTF_8); - return "redirect:/" + url; - } - } - if (paramSearch != null && paramSearch.length() > 64) { - paramSearch = null; - } - - int privacy = 0; - if (visitor.getUid() > 0) { - if (user.getUid() == visitor.getUid() || visitor.getUid() == 1) { - privacy = -3; - } else if (userService.isInWL(user.getUid(), visitor.getUid())) { - privacy = -2; - } - } - - String title; - if (paramShow == null) { - if (paramTag != null) { - title = "Блог " + user.getName() + ": *" + StringEscapeUtils.escapeHtml4(paramTag.getName()); - mids = messagesService.getUserTag(user.getUid(), paramTag.TID, privacy, before); - } else if (paramSearch != null) { - title = "Блог " + user.getName() + ": " + StringEscapeUtils.escapeHtml4(paramSearch); - mids = messagesService.getUserSearch(user.getUid(), Utils.encodeSphinx(paramSearch), privacy, before); - } else { - title = "Блог " + user.getName(); - mids = messagesService.getUserBlog(user.getUid(), privacy, before); - } - } else if (paramShow.equals("recomm")) { - title = "Рекомендации " + user.getName(); - mids = messagesService.getUserRecommendations(user.getUid(), before); - } else if (paramShow.equals("photos")) { - title = "Фотографии " + user.getName(); - mids = messagesService.getUserPhotos(user.getUid(), privacy, before); - } else { - throw new HttpNotFoundException(); - } - - String head = ""; - if (paramTag != null && tagService.getTagNoIndex(paramTag.TID)) { - head += ""; - } else if (before > 0 || paramShow != null) { - head += ""; - } - model.addAttribute("pageUrl", "http://juick.com/" + user.getName()); - model.addAttribute("title", title); - model.addAttribute("headers", head); - model.addAttribute("visitor", visitor); - model.addAttribute("noindex", paramShow == null && before == 0); - fillUserModel(model, user, visitor); - model.addAttribute("paramTag", paramTag); - List msgs = messagesService.getMessages(mids); - - if (visitor.getUid() != 0) { - List blUIDs = userService.checkBL(visitor.getUid(), - msgs.stream().map(m -> m.getUser().getUid()).collect(Collectors.toList())); - msgs.forEach(m -> m.ReadOnly |= blUIDs.contains(m.getUser().getUid())); - } - model.addAttribute("msgs", msgs); - model.addAttribute("headers", head); - model.addAttribute("showAdv", - paramShow == null && before == 0 && paramSearch == null && visitor.getUid() == 0); - if (mids.size() >= 20) { - String nextpage = "?before=" + mids.get(mids.size() - 1); - if (paramShow != null) { - nextpage += "&show=" + paramShow; - } - if (paramSearch != null) { - nextpage += "&search=" + URLEncoder.encode(paramSearch, CharEncoding.UTF_8); - } - if (paramTag != null) { - nextpage += "&tag=" + URLEncoder.encode(paramTag.getName(), CharEncoding.UTF_8); - } - model.addAttribute("nextpage", nextpage); - } - UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequestUri().build(); - String queryString = builder.getQuery(); - String requestURI = builder.toUri().getPath(); - if (sape != null && visitor.getUid() == 0 && queryString == null) { - String links = sape.getPageLinks(requestURI, sapeCookie).render(); - model.addAttribute("links", links); - } - return "views/blog"; - } - - @GetMapping("/{uname}/tags") - protected String doGetTags(@PathVariable String uname, ModelMap model) throws IOException { - com.juick.User user = userService.getUserByName(uname); - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isBanned()) { - throw new HttpNotFoundException(); - } - - model.addAttribute("title", "Теги " + user.getName()); - model.addAttribute("headers", ""); - model.addAttribute("visitor", visitor); - fillUserModel(model, user, visitor); - model.addAttribute("tags", tagService.getUserTagStats(user.getUid()).stream() - .sorted((e1, e2) -> Integer.compare(e2.getUsageCount(), e1.getUsageCount())).map(t -> t.getTag().getName()).collect(Collectors.toList())); - - return "views/blog_tags"; - } - - @GetMapping("/{uname}/friends") - protected String doGetFriends(@PathVariable String uname, ModelMap model) throws IOException { - com.juick.User user = userService.getUserByName(uname); - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isBanned()) { - throw new HttpNotFoundException(); - } - model.addAttribute("title", "Подписки " + user.getName()); - model.addAttribute("headers", ""); - model.addAttribute("visitor", visitor); - fillUserModel(model, user, visitor); - model.addAttribute("users", userService.getUserFriends(user.getUid())); - - return "views/users"; - } - - @GetMapping("/{uname}/readers") - protected String doGetReaders(@PathVariable String uname, ModelMap model) throws IOException { - com.juick.User user = userService.getUserByName(uname); - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isBanned()) { - throw new HttpForbiddenException(); - } - model.addAttribute("title", "Читатели " + user.getName()); - model.addAttribute("headers", ""); - model.addAttribute("visitor", visitor); - fillUserModel(model, user, visitor); - model.addAttribute("users", userService.getUserReaders(user.getUid())); - - return "views/users"; - } - - @GetMapping("/{uname}/bl") - protected String doGetBL(@PathVariable String uname, ModelMap model) throws IOException { - com.juick.User user = userService.getUserByName(uname); - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.isBanned() || visitor.getUid() != user.getUid()) { - throw new HttpForbiddenException(); - } - model.addAttribute("title", "Черный список " + user.getName()); - model.addAttribute("headers", ""); - model.addAttribute("visitor", visitor); - fillUserModel(model, user, visitor); - model.addAttribute("users", userService.getUserBLUsers(user.getUid())); - - return "views/users"; - } - @GetMapping("/tag/{tagName}") - protected String tagAction(HttpServletRequest request, - @PathVariable String tagName, - @CookieValue(name = "sape_cookie", required = false, defaultValue = StringUtils.EMPTY) String sapeCookie, - @RequestParam(required = false, defaultValue = "0") int before, - ModelMap model) throws IOException { - com.juick.User visitor = UserUtils.getCurrentUser(); - - String paramTagStr = StringEscapeUtils.unescapeHtml4(tagName); - com.juick.Tag paramTag = tagService.getTag(paramTagStr, false); - if (paramTag == null) { - throw new HttpNotFoundException(); - } else if (paramTag.SynonymID > 0 && paramTag.TID != paramTag.SynonymID) { - com.juick.Tag synTag = tagService.getTag(paramTag.SynonymID); - String url = "/tag/" + URLEncoder.encode(StringEscapeUtils.escapeHtml4(synTag.getName()), CharEncoding.UTF_8); - if (request.getQueryString() != null) { - url += "?" + request.getQueryString(); - } - return "redirect:" + url; - } else if (!paramTag.getName().equals(paramTagStr)) { - String url = "/tag/" + URLEncoder.encode(StringEscapeUtils.escapeHtml4(paramTag.getName()), CharEncoding.UTF_8); - if (request.getQueryString() != null) { - url += "?" + request.getQueryString(); - } - return "redirect:" + url; - } - - int visitor_uid = visitor.getUid(); - - String title = "*" + StringEscapeUtils.escapeHtml4(paramTag.getName()); - model.addAttribute("title", title); - List mids = messagesService.getTag(paramTag.TID, visitor_uid, before, (visitor_uid == 0) ? 40 : 20); - List msgs = messagesService.getMessages(mids); - if (visitor.getUid() != 0) { - List blUIDs = userService.checkBL( - visitor.getUid(), - msgs.stream().map(m -> m.getUser().getUid()).collect(Collectors.toList()) - ); - msgs.forEach(m -> m.ReadOnly |= blUIDs.contains(m.getUser().getUid())); - fillUserModel(model, visitor, visitor); - } - - String head = StringUtils.EMPTY; - if (tagService.getTagNoIndex(paramTag.TID)) { - head = ""; - } else if (before > 0 || mids.size() < 5) { - head = ""; - } - model.addAttribute("headers", head); - model.addAttribute("visitor", visitor); - model.addAttribute("tag", paramTag); - model.addAttribute("title", title); - model.addAttribute("msgs", msgs); - model.addAttribute("tags", tagService.getPopularTags()); - model.addAttribute("noindex", before > 0); - model.addAttribute("showAdv", before == 0 && visitor.getUid() == 0); - model.addAttribute("isSubscribed", tagService.isSubscribed(visitor, paramTag)); - model.addAttribute("isInBL", tagService.isInBL(visitor, paramTag)); - if (mids.size() >= 20) { - String nextpage = "/tag/" + URLEncoder.encode(paramTag.getName(), CharEncoding.UTF_8) + "?before=" + mids.get(mids.size() - 1); - model.addAttribute("nextpage", nextpage); - } - UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequestUri().build(); - String queryString = builder.getQuery(); - String requestURI = builder.toUri().getPath(); - if (sape != null && visitor.getUid() == 0 && queryString == null) { - String links = sape.getPageLinks(requestURI, sapeCookie).render(); - model.addAttribute("links", links); - } - return "views/index"; - } - @GetMapping("/pm/inbox") - protected String doGetInbox(ModelMap model) { - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.getUid() == 0) { - return "redirect:/login"; - } - String title = "PM: Inbox"; - List msgs = pmQueriesService.getLastPMInbox(visitor.getUid()); - fillUserModel(model, visitor, visitor); - model.addAttribute("title", title); - model.addAttribute("visitor", visitor); - model.addAttribute("msgs", msgs); - model.addAttribute("tags", tagService.getPopularTags()); - return "views/pm_inbox"; - } - - @GetMapping("/pm/sent") - protected String doGetSent(@RequestParam(required = false) String uname, - ModelMap model) { - com.juick.User visitor = UserUtils.getCurrentUser(); - if (visitor.getUid() == 0) { - return "redirect:/login"; - } - String title = "PM: Sent"; - List msgs = pmQueriesService.getLastPMSent(visitor.getUid()); - - if (WebUtils.isNotUserName(uname)) { - uname = StringUtils.EMPTY; - } - fillUserModel(model, visitor, visitor); - model.addAttribute("title", title); - model.addAttribute("visitor", visitor); - model.addAttribute("msgs", msgs); - model.addAttribute("tags", tagService.getPopularTags()); - model.addAttribute("uname", uname); - return "views/pm_sent"; - } - @GetMapping("/{uname}/{mid}") - protected String threadAction(ModelMap model, - @PathVariable String uname, - @PathVariable int mid, - @CookieValue(name = "sape_cookie", - required = false, defaultValue = StringUtils.EMPTY) String sapeCookie) { - com.juick.User visitor = UserUtils.getCurrentUser(); - - if (!messagesService.canViewThread(mid, visitor.getUid())) { - throw new HttpForbiddenException(); - } - - com.juick.Message msg = messagesService.getMessage(mid); - - if (msg == null || msg.getUser().isBanned()) { - throw new HttpNotFoundException(); - } - - com.juick.User user = userService.getUserByName(uname); - if (user.getUid() == 0 || !msg.getUser().equals(user)) { - return String.format("redirect:/%s/%d", msg.getUser().getName(), mid); - } - msg.VisitorCanComment = visitor.getUid() > 0; - fillUserModel(model, user, visitor); - if (visitor.getUid() > 0) { - boolean isMsgAuthor = visitor.getUid() == msg.getUser().getUid(); - boolean isInBL = userService.isInBLAny(msg.getUser().getUid(), visitor.getUid()); - msg.VisitorCanComment = isMsgAuthor || !(msg.ReadOnly || isInBL); - } - model.addAttribute("msg", msg); - - String title = msg.getUser().getName() + ": " + MessageUtils.getTagsString(msg); - - model.addAttribute("title", title); - model.addAttribute("visitor", visitor); - String headers = ""; - String pageUrl = "https://juick.com/" + msg.getUser().getName() + "/" + msg.getMid(); - if (msg.Hidden) { - headers += ""; - } - String cardType = StringUtils.isNotEmpty(msg.getAttachmentType()) ? "summary_large_image" : "summary"; - if (StringUtils.isNotEmpty(msg.getAttachmentType())) { - // additional check in case of broken images - if (msg.getAttachment() != null) { - String msgImage = msg.getAttachment().getMedium().getUrl(); - headers += ""; - } - } else { - String msgImage ="https://i.juick.com/a/" + msg.getUser().getUid() + ".png"; - headers += ""; - } - model.addAttribute("ogtype", "article"); - String cardDescription = StringEscapeUtils.escapeHtml4(PlainTextFormatter.formatTwitterCard(msg)); - headers += "\n" + - "\n" + - "\n" + - "\n" + - "\n" + - "\n"; - String twitterName = crosspostService.getTwitterName(msg.getUser().getUid()); - if (StringUtils.isNotEmpty(twitterName)) { - headers += "\n"; - } - if (msg.getTags().size() > 0) { - headers += "\n"; - } - model.addAttribute("headers", headers); - model.addAttribute("visitorSubscribed", messagesService.isSubscribed(visitor.getUid(), msg.getMid())); - model.addAttribute("visitorInBL", userService.isInBL(msg.getUser().getUid(), visitor.getUid())); - model.addAttribute("recomm", messagesService.getMessageRecommendations(msg.getMid())); - List replies = messagesService.getReplies(msg.getMid()); - - List blUIDs = new ArrayList<>(); - for (Message reply : replies) { - if (reply.getUser().getUid() != msg.getUser().getUid() - && !blUIDs.contains(reply.getUser().getUid())) { - blUIDs.add(reply.getUser().getUid()); - } - reply.VisitorCanComment = visitor.getUid() > 0; - if (visitor.getUid() > 0) { - boolean isMsgAuthor = visitor.getUid() == msg.getUser().getUid(); - boolean isReplyAuthor = visitor.getUid() == reply.getUser().getUid(); - reply.VisitorCanComment = isMsgAuthor || (!msg.ReadOnly - && msg.VisitorCanComment && (isReplyAuthor || !userService.isInBLAny(visitor.getUid(), reply.getUser().getUid()))); - } - } - model.addAttribute("replies", replies); - model.addAttribute("showAdv", visitor.getUid() == 0); - UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequestUri().build(); - String queryString = builder.getQuery(); - String requestURI = builder.toUri().getPath(); - if (sape != null && visitor.getUid() == 0 && queryString == null) { - String links = sape.getPageLinks(requestURI, sapeCookie).render(); - model.addAttribute("links", links); - } - return "views/thread"; - } - - // when message id is not fit to int - @ExceptionHandler(NumberFormatException.class) - public ResponseEntity notFoundAction() { - return new ResponseEntity<>(HttpStatus.NOT_FOUND); - } -} diff --git a/juick-www/src/main/java/com/juick/www/controllers/MessagesWWW.java b/juick-www/src/main/java/com/juick/www/controllers/MessagesWWW.java new file mode 100644 index 00000000..e6662c4e --- /dev/null +++ b/juick-www/src/main/java/com/juick/www/controllers/MessagesWWW.java @@ -0,0 +1,576 @@ +/* + * Copyright (C) 2008-2017, Juick + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +package com.juick.www.controllers; + +import com.juick.Message; +import com.juick.Tag; +import com.juick.formatters.PlainTextFormatter; +import com.juick.server.util.HttpForbiddenException; +import com.juick.server.util.HttpNotFoundException; +import com.juick.server.util.UserUtils; +import com.juick.server.util.WebUtils; +import com.juick.service.*; +import com.juick.util.MessageUtils; +import com.juick.www.Utils; +import org.apache.commons.codec.CharEncoding; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; +import org.apache.commons.text.StringEscapeUtils; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.ModelMap; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; +import org.springframework.web.util.UriComponents; +import ru.sape.Sape; + +import javax.inject.Inject; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.net.URLEncoder; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * + * @author Ugnich Anton + */ +@Controller +public class MessagesWWW { + @Inject + private UserService userService; + @Inject + private TagService tagService; + @Inject + private MessagesService messagesService; + @Inject + private Sape sape; + @Inject + private PMQueriesService pmQueriesService; + @Inject + private CrosspostService crosspostService; + + void fillUserModel(ModelMap model, com.juick.User user, com.juick.User visitor) { + model.addAttribute("user", user); + model.addAttribute("isSubscribed", userService.isSubscribed(visitor.getUid(), user.getUid())); + model.addAttribute("isInBL", userService.isInBL(visitor.getUid(), user.getUid())); + model.addAttribute("isInBLAny", userService.isInBLAny(user.getUid(), visitor.getUid())); + model.addAttribute("statsIRead", userService.getStatsIRead(user.getUid())); + model.addAttribute("statsMyReaders", userService.getStatsMyReaders(user.getUid())); + model.addAttribute("statsMyBL", userService.getUserBLUsers(user.getUid()).size()); + model.addAttribute("statsMessages", userService.getStatsMessages(user.getUid())); + model.addAttribute("statsReplies", userService.getStatsReplies(user.getUid())); + model.addAttribute("iread", userService.getUserReadLeastPopular(user.getUid(), 8)); + model.addAttribute("tagStats", tagService.getUserTagStats(user.getUid()) + .stream().sorted((e1, e2) -> Integer.compare(e2.getUsageCount(), e1.getUsageCount())).limit(20).map(t -> t.getTag().getName()).collect(Collectors.toList())); + } + + @GetMapping("/") + protected String doGet( + @RequestParam(required = false) String tag, + @RequestParam(name = "show", required = false) String paramShow, + @RequestParam(name = "search", required = false) String paramSearch, + @RequestParam(name = "before", required = false, defaultValue = "0") Integer paramBefore, + @RequestParam(name = "to", required = false, defaultValue = "0") Long paramTo, + @CookieValue(name = "sape_cookie", required = false, defaultValue = StringUtils.EMPTY) String sapeCookie, + ModelMap model) throws IOException { + if (tag != null) { + return "redirect:/tag/" + URLEncoder.encode(tag, CharEncoding.UTF_8); + } + com.juick.User visitor = UserUtils.getCurrentUser(); + + if (paramSearch != null && paramSearch.length() > 64) { + paramSearch = null; + } + + model.addAttribute("discover", false); + + String title; + List mids; + + if (paramSearch != null) { + title = "Поиск: " + StringEscapeUtils.escapeHtml4(paramSearch); + mids = messagesService.getSearch(Utils.encodeSphinx(paramSearch), paramBefore); + } else if (paramShow == null) { + if (visitor.getUid() > 0) { + title = "Популярные"; + mids = messagesService.getPopular(visitor.getUid(), paramBefore); + model.addAttribute("discover", true); + } else { + title = "Микроблоги Juick: популярные записи"; + mids = messagesService.getPopular(0, paramBefore); + } + + } else if (paramShow.equals("top")) { + return "redirect:/"; + } else if (paramShow.equals("my") && !visitor.isAnonymous()) { + title = "Моя лента"; + mids = messagesService.getMyFeed(visitor.getUid(), paramBefore, true); + } else if (paramShow.equals("private") && !visitor.isAnonymous()) { + title = "Приватные"; + mids = messagesService.getPrivate(visitor.getUid(), paramBefore); + } else if (paramShow.equals("discuss") && !visitor.isAnonymous()) { + title = "Обсуждения"; + mids = messagesService.getDiscussions(visitor.getUid(), paramTo); + } else if (paramShow.equals("recommended") && !visitor.isAnonymous()) { + title = "Рекомендации"; + mids = messagesService.getRecommended(visitor.getUid(), paramBefore); + } else if (paramShow.equals("photos")) { + title = "Фотографии"; + mids = messagesService.getPhotos(visitor.getUid(), paramBefore); + model.addAttribute("discover", true); + } else if (paramShow.equals("all")) { + title = "Все сообщения"; + mids = messagesService.getAll(visitor.getUid(), paramBefore); + model.addAttribute("discover", true); + } else { + throw new HttpNotFoundException(); + } + + String head = StringUtils.EMPTY; + if (paramBefore > 0 || paramShow != null) { + head = ""; + } + model.addAttribute("title", title); + model.addAttribute("headers", head); + model.addAttribute("visitor", visitor); + model.addAttribute("noindex", !(paramShow == null && paramBefore == 0)); + List msgs = messagesService.getMessages(mids); + + if (visitor.getUid() != 0) { + fillUserModel(model, visitor, visitor); + List blUIDs = userService.checkBL(visitor.getUid(), + msgs.stream().map(m -> m.getUser().getUid()).collect(Collectors.toList())); + msgs.forEach(m -> m.ReadOnly |= blUIDs.contains(m.getUser().getUid())); + } + model.addAttribute("msgs", msgs); + model.addAttribute("tags", tagService.getPopularTags()); + model.addAttribute("headers", head); + model.addAttribute("showAdv", + paramShow == null && paramBefore == 0 && paramSearch == null && visitor.getUid() == 0); + if (mids.size() >= 20) { + String nextpage = (paramShow != null && paramShow.equals("discuss")) ? "?to=" + msgs.get(msgs.size() - 1).getUpdated().toEpochMilli() : "?before=" + mids.get(mids.size() - 1); + if (paramShow != null) { + nextpage += "&show=" + paramShow; + } + if (paramSearch != null) { + nextpage += "&search=" + URLEncoder.encode(paramSearch, CharEncoding.UTF_8); + } + model.addAttribute("nextpage", nextpage); + } + UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequestUri().build(); + String queryString = builder.getQuery(); + String requestURI = builder.toUri().getPath(); + if (sape != null && visitor.getUid() == 0 && queryString == null) { + String links = sape.getPageLinks(requestURI, sapeCookie).render(); + model.addAttribute("links", links); + } + return "views/index"; + } + + @GetMapping("/{uname}/") + protected String doGetBlog( + @RequestParam(required = false, name = "show") String paramShow, + @RequestParam(required = false, name = "tag") String paramTagStr, + @RequestParam(required = false, name = "search") String paramSearch, + @PathVariable String uname, + @RequestParam(required = false, defaultValue = "0") Integer before, + @CookieValue(name = "sape_cookie", required = false, defaultValue = StringUtils.EMPTY) String sapeCookie, + ModelMap model) throws IOException { + com.juick.User user = userService.getUserByName(uname); + com.juick.User visitor = UserUtils.getCurrentUser(); + if (user.isBanned() || user.isAnonymous()) { + throw new HttpNotFoundException(); + } + + List mids; + + com.juick.Tag paramTag = null; + if (paramTagStr != null) { + if (paramTagStr.length() < 64) { + paramTag = tagService.getTag(paramTagStr, false); + } + if (paramTag == null) { + throw new HttpNotFoundException(); + } else if (!paramTag.getName().equals(paramTagStr)) { + String url = user.getName() + "/?tag=" + URLEncoder.encode(paramTag.getName(), CharEncoding.UTF_8); + return "redirect:/" + url; + } + } + if (paramSearch != null && paramSearch.length() > 64) { + paramSearch = null; + } + + int privacy = 0; + if (visitor.getUid() > 0) { + if (user.getUid() == visitor.getUid() || visitor.getUid() == 1) { + privacy = -3; + } else if (userService.isInWL(user.getUid(), visitor.getUid())) { + privacy = -2; + } + } + + String title; + if (paramShow == null) { + if (paramTag != null) { + title = "Блог " + user.getName() + ": *" + StringEscapeUtils.escapeHtml4(paramTag.getName()); + mids = messagesService.getUserTag(user.getUid(), paramTag.TID, privacy, before); + } else if (paramSearch != null) { + title = "Блог " + user.getName() + ": " + StringEscapeUtils.escapeHtml4(paramSearch); + mids = messagesService.getUserSearch(user.getUid(), Utils.encodeSphinx(paramSearch), privacy, before); + } else { + title = "Блог " + user.getName(); + mids = messagesService.getUserBlog(user.getUid(), privacy, before); + } + } else if (paramShow.equals("recomm")) { + title = "Рекомендации " + user.getName(); + mids = messagesService.getUserRecommendations(user.getUid(), before); + } else if (paramShow.equals("photos")) { + title = "Фотографии " + user.getName(); + mids = messagesService.getUserPhotos(user.getUid(), privacy, before); + } else { + throw new HttpNotFoundException(); + } + + String head = ""; + if (paramTag != null && tagService.getTagNoIndex(paramTag.TID)) { + head += ""; + } else if (before > 0 || paramShow != null) { + head += ""; + } + model.addAttribute("pageUrl", "http://juick.com/" + user.getName()); + model.addAttribute("title", title); + model.addAttribute("headers", head); + model.addAttribute("visitor", visitor); + model.addAttribute("noindex", paramShow == null && before == 0); + fillUserModel(model, user, visitor); + model.addAttribute("paramTag", paramTag); + List msgs = messagesService.getMessages(mids); + + if (visitor.getUid() != 0) { + List blUIDs = userService.checkBL(visitor.getUid(), + msgs.stream().map(m -> m.getUser().getUid()).collect(Collectors.toList())); + msgs.forEach(m -> m.ReadOnly |= blUIDs.contains(m.getUser().getUid())); + } + model.addAttribute("msgs", msgs); + model.addAttribute("headers", head); + model.addAttribute("showAdv", + paramShow == null && before == 0 && paramSearch == null && visitor.getUid() == 0); + if (mids.size() >= 20) { + String nextpage = "?before=" + mids.get(mids.size() - 1); + if (paramShow != null) { + nextpage += "&show=" + paramShow; + } + if (paramSearch != null) { + nextpage += "&search=" + URLEncoder.encode(paramSearch, CharEncoding.UTF_8); + } + if (paramTag != null) { + nextpage += "&tag=" + URLEncoder.encode(paramTag.getName(), CharEncoding.UTF_8); + } + model.addAttribute("nextpage", nextpage); + } + UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequestUri().build(); + String queryString = builder.getQuery(); + String requestURI = builder.toUri().getPath(); + if (sape != null && visitor.getUid() == 0 && queryString == null) { + String links = sape.getPageLinks(requestURI, sapeCookie).render(); + model.addAttribute("links", links); + } + return "views/blog"; + } + + @GetMapping("/{uname}/tags") + protected String doGetTags(@PathVariable String uname, ModelMap model) throws IOException { + com.juick.User user = userService.getUserByName(uname); + com.juick.User visitor = UserUtils.getCurrentUser(); + if (visitor.isBanned()) { + throw new HttpNotFoundException(); + } + + model.addAttribute("title", "Теги " + user.getName()); + model.addAttribute("headers", ""); + model.addAttribute("visitor", visitor); + fillUserModel(model, user, visitor); + model.addAttribute("tags", tagService.getUserTagStats(user.getUid()).stream() + .sorted((e1, e2) -> Integer.compare(e2.getUsageCount(), e1.getUsageCount())).map(t -> t.getTag().getName()).collect(Collectors.toList())); + + return "views/blog_tags"; + } + + @GetMapping("/{uname}/friends") + protected String doGetFriends(@PathVariable String uname, ModelMap model) throws IOException { + com.juick.User user = userService.getUserByName(uname); + com.juick.User visitor = UserUtils.getCurrentUser(); + if (visitor.isBanned()) { + throw new HttpNotFoundException(); + } + model.addAttribute("title", "Подписки " + user.getName()); + model.addAttribute("headers", ""); + model.addAttribute("visitor", visitor); + fillUserModel(model, user, visitor); + model.addAttribute("users", userService.getUserFriends(user.getUid())); + + return "views/users"; + } + + @GetMapping("/{uname}/readers") + protected String doGetReaders(@PathVariable String uname, ModelMap model) throws IOException { + com.juick.User user = userService.getUserByName(uname); + com.juick.User visitor = UserUtils.getCurrentUser(); + if (visitor.isBanned()) { + throw new HttpForbiddenException(); + } + model.addAttribute("title", "Читатели " + user.getName()); + model.addAttribute("headers", ""); + model.addAttribute("visitor", visitor); + fillUserModel(model, user, visitor); + model.addAttribute("users", userService.getUserReaders(user.getUid())); + + return "views/users"; + } + + @GetMapping("/{uname}/bl") + protected String doGetBL(@PathVariable String uname, ModelMap model) throws IOException { + com.juick.User user = userService.getUserByName(uname); + com.juick.User visitor = UserUtils.getCurrentUser(); + if (visitor.isBanned() || visitor.getUid() != user.getUid()) { + throw new HttpForbiddenException(); + } + model.addAttribute("title", "Черный список " + user.getName()); + model.addAttribute("headers", ""); + model.addAttribute("visitor", visitor); + fillUserModel(model, user, visitor); + model.addAttribute("users", userService.getUserBLUsers(user.getUid())); + + return "views/users"; + } + @GetMapping("/tag/{tagName}") + protected String tagAction(HttpServletRequest request, + @PathVariable String tagName, + @CookieValue(name = "sape_cookie", required = false, defaultValue = StringUtils.EMPTY) String sapeCookie, + @RequestParam(required = false, defaultValue = "0") int before, + ModelMap model) throws IOException { + com.juick.User visitor = UserUtils.getCurrentUser(); + + String paramTagStr = StringEscapeUtils.unescapeHtml4(tagName); + com.juick.Tag paramTag = tagService.getTag(paramTagStr, false); + if (paramTag == null) { + throw new HttpNotFoundException(); + } else if (paramTag.SynonymID > 0 && paramTag.TID != paramTag.SynonymID) { + com.juick.Tag synTag = tagService.getTag(paramTag.SynonymID); + String url = "/tag/" + URLEncoder.encode(StringEscapeUtils.escapeHtml4(synTag.getName()), CharEncoding.UTF_8); + if (request.getQueryString() != null) { + url += "?" + request.getQueryString(); + } + return "redirect:" + url; + } else if (!paramTag.getName().equals(paramTagStr)) { + String url = "/tag/" + URLEncoder.encode(StringEscapeUtils.escapeHtml4(paramTag.getName()), CharEncoding.UTF_8); + if (request.getQueryString() != null) { + url += "?" + request.getQueryString(); + } + return "redirect:" + url; + } + + int visitor_uid = visitor.getUid(); + + String title = "*" + StringEscapeUtils.escapeHtml4(paramTag.getName()); + model.addAttribute("title", title); + List mids = messagesService.getTag(paramTag.TID, visitor_uid, before, (visitor_uid == 0) ? 40 : 20); + List msgs = messagesService.getMessages(mids); + if (visitor.getUid() != 0) { + List blUIDs = userService.checkBL( + visitor.getUid(), + msgs.stream().map(m -> m.getUser().getUid()).collect(Collectors.toList()) + ); + msgs.forEach(m -> m.ReadOnly |= blUIDs.contains(m.getUser().getUid())); + fillUserModel(model, visitor, visitor); + } + + String head = StringUtils.EMPTY; + if (tagService.getTagNoIndex(paramTag.TID)) { + head = ""; + } else if (before > 0 || mids.size() < 5) { + head = ""; + } + model.addAttribute("headers", head); + model.addAttribute("visitor", visitor); + model.addAttribute("tag", paramTag); + model.addAttribute("title", title); + model.addAttribute("msgs", msgs); + model.addAttribute("tags", tagService.getPopularTags()); + model.addAttribute("noindex", before > 0); + model.addAttribute("showAdv", before == 0 && visitor.getUid() == 0); + model.addAttribute("isSubscribed", tagService.isSubscribed(visitor, paramTag)); + model.addAttribute("isInBL", tagService.isInBL(visitor, paramTag)); + if (mids.size() >= 20) { + String nextpage = "/tag/" + URLEncoder.encode(paramTag.getName(), CharEncoding.UTF_8) + "?before=" + mids.get(mids.size() - 1); + model.addAttribute("nextpage", nextpage); + } + UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequestUri().build(); + String queryString = builder.getQuery(); + String requestURI = builder.toUri().getPath(); + if (sape != null && visitor.getUid() == 0 && queryString == null) { + String links = sape.getPageLinks(requestURI, sapeCookie).render(); + model.addAttribute("links", links); + } + return "views/index"; + } + @GetMapping("/pm/inbox") + protected String doGetInbox(ModelMap model) { + com.juick.User visitor = UserUtils.getCurrentUser(); + if (visitor.getUid() == 0) { + return "redirect:/login"; + } + String title = "PM: Inbox"; + List msgs = pmQueriesService.getLastPMInbox(visitor.getUid()); + fillUserModel(model, visitor, visitor); + model.addAttribute("title", title); + model.addAttribute("visitor", visitor); + model.addAttribute("msgs", msgs); + model.addAttribute("tags", tagService.getPopularTags()); + return "views/pm_inbox"; + } + + @GetMapping("/pm/sent") + protected String doGetSent(@RequestParam(required = false) String uname, + ModelMap model) { + com.juick.User visitor = UserUtils.getCurrentUser(); + if (visitor.getUid() == 0) { + return "redirect:/login"; + } + String title = "PM: Sent"; + List msgs = pmQueriesService.getLastPMSent(visitor.getUid()); + + if (WebUtils.isNotUserName(uname)) { + uname = StringUtils.EMPTY; + } + fillUserModel(model, visitor, visitor); + model.addAttribute("title", title); + model.addAttribute("visitor", visitor); + model.addAttribute("msgs", msgs); + model.addAttribute("tags", tagService.getPopularTags()); + model.addAttribute("uname", uname); + return "views/pm_sent"; + } + @GetMapping("/{uname}/{mid}") + protected String threadAction(ModelMap model, + @PathVariable String uname, + @PathVariable int mid, + @CookieValue(name = "sape_cookie", + required = false, defaultValue = StringUtils.EMPTY) String sapeCookie) { + com.juick.User visitor = UserUtils.getCurrentUser(); + + if (!messagesService.canViewThread(mid, visitor.getUid())) { + throw new HttpForbiddenException(); + } + + com.juick.Message msg = messagesService.getMessage(mid); + + if (msg == null || msg.getUser().isBanned()) { + throw new HttpNotFoundException(); + } + + com.juick.User user = userService.getUserByName(uname); + if (user.getUid() == 0 || !msg.getUser().equals(user)) { + return String.format("redirect:/%s/%d", msg.getUser().getName(), mid); + } + msg.VisitorCanComment = visitor.getUid() > 0; + fillUserModel(model, user, visitor); + if (visitor.getUid() > 0) { + boolean isMsgAuthor = visitor.getUid() == msg.getUser().getUid(); + boolean isInBL = userService.isInBLAny(msg.getUser().getUid(), visitor.getUid()); + msg.VisitorCanComment = isMsgAuthor || !(msg.ReadOnly || isInBL); + } + model.addAttribute("msg", msg); + + String title = msg.getUser().getName() + ": " + MessageUtils.getTagsString(msg); + + model.addAttribute("title", title); + model.addAttribute("visitor", visitor); + String headers = ""; + String pageUrl = "https://juick.com/" + msg.getUser().getName() + "/" + msg.getMid(); + if (msg.Hidden) { + headers += ""; + } + String cardType = StringUtils.isNotEmpty(msg.getAttachmentType()) ? "summary_large_image" : "summary"; + if (StringUtils.isNotEmpty(msg.getAttachmentType())) { + // additional check in case of broken images + if (msg.getAttachment() != null) { + String msgImage = msg.getAttachment().getMedium().getUrl(); + headers += ""; + } + } else { + String msgImage ="https://i.juick.com/a/" + msg.getUser().getUid() + ".png"; + headers += ""; + } + model.addAttribute("ogtype", "article"); + String cardDescription = StringEscapeUtils.escapeHtml4(PlainTextFormatter.formatTwitterCard(msg)); + headers += "\n" + + "\n" + + "\n" + + "\n" + + "\n" + + "\n"; + String twitterName = crosspostService.getTwitterName(msg.getUser().getUid()); + if (StringUtils.isNotEmpty(twitterName)) { + headers += "\n"; + } + if (msg.getTags().size() > 0) { + headers += "\n"; + } + model.addAttribute("headers", headers); + model.addAttribute("visitorSubscribed", messagesService.isSubscribed(visitor.getUid(), msg.getMid())); + model.addAttribute("visitorInBL", userService.isInBL(msg.getUser().getUid(), visitor.getUid())); + model.addAttribute("recomm", messagesService.getMessageRecommendations(msg.getMid())); + List replies = messagesService.getReplies(msg.getMid()); + + List blUIDs = new ArrayList<>(); + for (Message reply : replies) { + if (reply.getUser().getUid() != msg.getUser().getUid() + && !blUIDs.contains(reply.getUser().getUid())) { + blUIDs.add(reply.getUser().getUid()); + } + reply.VisitorCanComment = visitor.getUid() > 0; + if (visitor.getUid() > 0) { + boolean isMsgAuthor = visitor.getUid() == msg.getUser().getUid(); + boolean isReplyAuthor = visitor.getUid() == reply.getUser().getUid(); + reply.VisitorCanComment = isMsgAuthor || (!msg.ReadOnly + && msg.VisitorCanComment && (isReplyAuthor || !userService.isInBLAny(visitor.getUid(), reply.getUser().getUid()))); + } + } + model.addAttribute("replies", replies); + model.addAttribute("showAdv", visitor.getUid() == 0); + UriComponents builder = ServletUriComponentsBuilder.fromCurrentRequestUri().build(); + String queryString = builder.getQuery(); + String requestURI = builder.toUri().getPath(); + if (sape != null && visitor.getUid() == 0 && queryString == null) { + String links = sape.getPageLinks(requestURI, sapeCookie).render(); + model.addAttribute("links", links); + } + return "views/thread"; + } + + // when message id is not fit to int + @ExceptionHandler(NumberFormatException.class) + public ResponseEntity notFoundAction() { + return new ResponseEntity<>(HttpStatus.NOT_FOUND); + } +} diff --git a/juick-www/src/main/java/com/juick/www/controllers/NewMessage.java b/juick-www/src/main/java/com/juick/www/controllers/NewMessage.java index f8a11d82..6e16ae50 100644 --- a/juick-www/src/main/java/com/juick/www/controllers/NewMessage.java +++ b/juick-www/src/main/java/com/juick/www/controllers/NewMessage.java @@ -16,13 +16,10 @@ */ package com.juick.www.controllers; -import com.juick.Status; -import com.juick.Tag; import com.juick.User; import com.juick.server.helpers.AnonymousUser; import com.juick.server.util.*; import com.juick.service.*; -import com.juick.util.MessageUtils; import com.juick.www.WebApp; import org.apache.commons.lang3.StringUtils; import org.apache.commons.text.StringEscapeUtils; @@ -34,7 +31,6 @@ import org.springframework.ui.ModelMap; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.multipart.MultipartFile; import rocks.xmpp.addr.Jid; import rocks.xmpp.core.stanza.model.Message; @@ -47,7 +43,6 @@ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; -import java.util.List; import java.util.stream.Collectors; /** @@ -76,6 +71,8 @@ public class NewMessage { private String imgDir; @Value("${upload_tmp_dir:#{systemEnvironment['TEMP'] ?: '/tmp'}}") private String tmpDir; + @Value("${xmppbot_jid:juick@localhost}") + private Jid botJid; private static final Logger logger = LoggerFactory.getLogger(NewMessage.class); @@ -247,11 +244,11 @@ public class NewMessage { if (visitor.getUid() == 0 || visitor.isBanned()) { throw new HttpForbiddenException(); } - String body = bodyParam.replace("\r", StringUtils.EMPTY); + String body = StringUtils.isNotEmpty(bodyParam) ? bodyParam.replace("\r", StringUtils.EMPTY) : StringUtils.EMPTY; String attachmentFName = HttpUtils.receiveMultiPartFile(attach, tmpDir); - if (StringUtils.isBlank(attachmentFName) && img != null && img.length() > 10) { + if (StringUtils.isBlank(attachmentFName) && StringUtils.isNotBlank(img)) { try { URL imgUrl = new URL(img); attachmentFName = HttpUtils.downloadImage(imgUrl, tmpDir); @@ -263,7 +260,7 @@ public class NewMessage { Message msg = new Message(); msg.setType(Message.Type.CHAT); msg.setFrom(Jid.of(String.valueOf(visitor.getUid()), "uid.juick.com", "perl")); - msg.setTo(Jid.of("juick@juick.com/Juick")); + msg.setTo(botJid); msg.setBody(body); try { if (StringUtils.isNotEmpty(attachmentFName)) { -- cgit v1.2.3