/*
* Copyright 1998-2014 Linux.org.ru
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ru.org.linux.topic;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import org.apache.commons.lang.StringEscapeUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.Errors;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
import ru.org.linux.auth.AccessViolationException;
import ru.org.linux.auth.CaptchaService;
import ru.org.linux.auth.IPBlockDao;
import ru.org.linux.auth.IPBlockInfo;
import ru.org.linux.edithistory.EditHistoryDto;
import ru.org.linux.edithistory.EditHistoryObjectTypeEnum;
import ru.org.linux.edithistory.EditHistoryService;
import ru.org.linux.group.Group;
import ru.org.linux.group.GroupDao;
import ru.org.linux.group.GroupPermissionService;
import ru.org.linux.poll.Poll;
import ru.org.linux.poll.PollDao;
import ru.org.linux.poll.PollNotFoundException;
import ru.org.linux.poll.PollVariant;
import ru.org.linux.search.SearchQueueSender;
import ru.org.linux.section.Section;
import ru.org.linux.site.BadInputException;
import ru.org.linux.site.Template;
import ru.org.linux.spring.FeedPinger;
import ru.org.linux.spring.dao.MsgbaseDao;
import ru.org.linux.tag.TagName;
import ru.org.linux.tag.TagRef;
import ru.org.linux.tag.TagService;
import ru.org.linux.user.Profile;
import ru.org.linux.user.User;
import ru.org.linux.user.UserErrorException;
import ru.org.linux.util.ExceptionBindingErrorProcessor;
import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Controller
public class EditTopicController {
@Autowired
private SearchQueueSender searchQueueSender;
@Autowired
private FeedPinger feedPinger;
@Autowired
private TopicDao messageDao;
@Autowired
private TopicService topicService;
@Autowired
private TopicPrepareService prepareService;
@Autowired
private GroupDao groupDao;
@Autowired
private PollDao pollDao;
@Autowired
private GroupPermissionService permissionService;
@Autowired
private MsgbaseDao msgbaseDao;
@Autowired
private EditHistoryService editHistoryService;
@Autowired
private EditTopicRequestValidator editTopicRequestValidator;
@Autowired
private IPBlockDao ipBlockDao;
@Autowired
private CaptchaService captcha;
@RequestMapping(value = "/commit.jsp", method = RequestMethod.GET)
public ModelAndView showCommitForm(
HttpServletRequest request,
@RequestParam("msgid") int msgid,
@ModelAttribute("form") EditTopicRequest form
) throws Exception {
Template tmpl = Template.getTemplate(request);
if (!tmpl.isModeratorSession()) {
throw new AccessViolationException("Not authorized");
}
Topic message = messageDao.getById(msgid);
if (message.isCommited()) {
throw new UserErrorException("Сообщение уже подтверждено");
}
PreparedTopic preparedMessage = prepareService.prepareTopic(message, request.isSecure(), tmpl.getCurrentUser());
if (!preparedMessage.getSection().isPremoderated()) {
throw new UserErrorException("Раздел не премодерируемый");
}
ModelAndView mv = prepareModel(
preparedMessage,
form,
tmpl.getCurrentUser(),
request.isSecure(),
tmpl.getProf()
);
mv.getModel().put("commit", true);
return mv;
}
@RequestMapping(value = "/edit.jsp", method = RequestMethod.GET)
public ModelAndView showEditForm(
ServletRequest request,
@RequestParam("msgid") int msgid,
@ModelAttribute("form") EditTopicRequest form
) throws Exception {
Template tmpl = Template.getTemplate(request);
if (!tmpl.isSessionAuthorized()) {
throw new AccessViolationException("Not authorized");
}
Topic message = messageDao.getById(msgid);
User user = tmpl.getCurrentUser();
PreparedTopic preparedMessage = prepareService.prepareTopic(message, request.isSecure(), tmpl.getCurrentUser());
if (!permissionService.isEditable(preparedMessage, user) && !permissionService.isTagsEditable(preparedMessage, user)) {
throw new AccessViolationException("это сообщение нельзя править");
}
return prepareModel(
preparedMessage,
form,
tmpl.getCurrentUser(),
request.isSecure(),
tmpl.getProf()
);
}
private ModelAndView prepareModel(
PreparedTopic preparedTopic,
EditTopicRequest form,
User currentUser,
boolean secure,
Profile profile
) throws PollNotFoundException {
Map<String, Object> params = new HashMap<>();
final Topic message = preparedTopic.getMessage();
params.put("message", message);
params.put("preparedMessage", preparedTopic);
Group group = preparedTopic.getGroup();
params.put("group", group);
params.put("groups", groupDao.getGroups(preparedTopic.getSection()));
params.put("newMsg", message);
TopicMenu topicMenu = prepareService.getTopicMenu(
preparedTopic,
currentUser,
secure,
profile,
true
);
params.put("topicMenu", topicMenu);
List<EditHistoryDto> editInfoList = editHistoryService.getEditInfo(message.getId(), EditHistoryObjectTypeEnum.TOPIC);
if (!editInfoList.isEmpty()) {
params.put("editInfo", editInfoList.get(0));
ImmutableSet<User> editors = editHistoryService.getEditorUsers(message, editInfoList);
ImmutableMap.Builder<Integer,Integer> editorBonus = ImmutableMap.builder();
for (User editor : editors) {
if (currentUser.getId() == editor.getId()) {
editorBonus.put(editor.getId(), 0);
} else {
editorBonus.put(editor.getId(), 1);
}
}
form.setEditorBonus(editorBonus.build());
params.put("editors", editors);
}
params.put("commit", false);
if (group.isLinksAllowed()) {
form.setLinktext(message.getLinktext());
form.setUrl(message.getUrl());
}
form.setTitle(StringEscapeUtils.unescapeHtml(message.getTitle()));
form.setMsg(msgbaseDao.getMessageText(message.getId()).getText());
if (message.getSectionId() == Section.SECTION_NEWS) {
form.setMinor(message.isMinor());
}
if (!preparedTopic.getTags().isEmpty()) {
form.setTags(TagRef.names(preparedTopic.getTags()));
}
if (preparedTopic.getSection().isPollPostAllowed()) {
Poll poll = pollDao.getPollByTopicId(message.getId());
form.setPoll(PollVariant.toMap(poll.getVariants()));
form.setMultiselect(poll.isMultiSelect());
}
return new ModelAndView("edit", params);
}
@ModelAttribute("ipBlockInfo")
private IPBlockInfo loadIPBlock(HttpServletRequest request) {
return ipBlockDao.getBlockInfo(request.getRemoteAddr());
}
@RequestMapping(value = "/edit.jsp", method = RequestMethod.POST)
public ModelAndView edit(
HttpServletRequest request,
@RequestParam("msgid") int msgid,
@RequestParam(value="lastEdit", required=false) Long lastEdit,
@RequestParam(value="chgrp", required=false) Integer changeGroupId,
@Valid @ModelAttribute("form") EditTopicRequest form,
Errors errors,
@ModelAttribute("ipBlockInfo") IPBlockInfo ipBlockInfo
) throws Exception {
Template tmpl = Template.getTemplate(request);
if (!tmpl.isSessionAuthorized()) {
throw new AccessViolationException("Not authorized");
}
Map<String, Object> params = new HashMap<>();
final Topic message = messageDao.getById(msgid);
PreparedTopic preparedTopic = prepareService.prepareTopic(message, request.isSecure(), tmpl.getCurrentUser());
Group group = preparedTopic.getGroup();
User user = tmpl.getCurrentUser();
IPBlockDao.checkBlockIP(ipBlockInfo, errors, user);
boolean tagsEditable = permissionService.isTagsEditable(preparedTopic, user);
boolean editable = permissionService.isEditable(preparedTopic, user);
if (!editable && !tagsEditable) {
throw new AccessViolationException("это сообщение нельзя править");
}
params.put("message", message);
params.put("preparedMessage", preparedTopic);
params.put("group", group);
params.put("topicMenu", prepareService.getTopicMenu(
preparedTopic,
tmpl.getCurrentUser(),
request.isSecure(),
tmpl.getProf(),
true
));
params.put("groups", groupDao.getGroups(preparedTopic.getSection()));
if (editable) {
String title = request.getParameter("title");
if (title == null || title.trim().isEmpty()) {
throw new BadInputException("заголовок сообщения не может быть пустым");
}
}
boolean preview = request.getParameter("preview") != null;
if (preview) {
params.put("info", "Предпросмотр");
}
boolean publish = request.getParameter("publish") != null;
List<EditHistoryDto> editInfoList = editHistoryService.getEditInfo(message.getId(), EditHistoryObjectTypeEnum.TOPIC);
if (!editInfoList.isEmpty()) {
EditHistoryDto editHistoryDto = editInfoList.get(0);
params.put("editInfo", editHistoryDto);
if (lastEdit == null || editHistoryDto.getEditdate().getTime()!=lastEdit) {
errors.reject(null, "Сообщение было отредактировано независимо");
}
}
boolean commit = request.getParameter("commit") != null;
if (commit) {
user.checkCommit();
if (message.isCommited()) {
throw new BadInputException("сообщение уже подтверждено");
}
}
params.put("commit", !message.isCommited() && preparedTopic.getSection().isPremoderated() && user.isModerator());
Topic newMsg = new Topic(group, message, form, publish);
boolean modified = false;
if (!message.getTitle().equals(newMsg.getTitle())) {
modified = true;
}
if (form.getMsg()!=null) {
String oldText = msgbaseDao.getMessageText(message.getId()).getText();
if (!oldText.equals(form.getMsg())) {
modified = true;
}
}
if (message.getLinktext() == null) {
if (newMsg.getLinktext() != null) {
modified = true;
}
} else if (!message.getLinktext().equals(newMsg.getLinktext())) {
modified = true;
}
if (group.isLinksAllowed()) {
if (message.getUrl() == null) {
if (newMsg.getUrl() != null) {
modified = true;
}
} else if (!message.getUrl().equals(newMsg.getUrl())) {
modified = true;
}
}
if (!editable && modified) {
throw new AccessViolationException("нельзя править это сообщение, только теги");
}
if (form.getMinor()!=null && !tmpl.isModeratorSession()) {
throw new AccessViolationException("вы не можете менять статус новости");
}
List<String> newTags = null;
if (form.getTags()!=null) {
newTags = TagName.parseAndSanitizeTags(form.getTags());
}
if (changeGroupId != null) {
if (message.getGroupId() != changeGroupId) {
Group changeGroup = groupDao.getGroup(changeGroupId);
int section = message.getSectionId();
if (changeGroup.getSectionId() != section) {
throw new AccessViolationException("Can't move topics between sections");
}
}
}
Poll newPoll = null;
if (preparedTopic.getSection().isPollPostAllowed() && form.getPoll() != null && tmpl.isModeratorSession()) {
newPoll = buildNewPoll(message, form);
}
String newText;
if (form.getMsg() != null) {
newText = form.getMsg();
} else {
newText = msgbaseDao.getMessageText(message.getId()).getText();
}
if (form.getEditorBonus() != null) {
ImmutableSet<Integer> editors = editHistoryService.getEditors(message, editInfoList);
for (int userid : form.getEditorBonus().keySet()) {
if (!editors.contains(userid)) {
errors.reject("editorBonus", "некорректный корректор?!");
}
}
}
if (!preview && !errors.hasErrors() && ipBlockInfo.isCaptchaRequired()) {
captcha.checkCaptcha(request, errors);
}
if (!preview && !errors.hasErrors()) {
boolean changed = topicService.updateAndCommit(
newMsg,
message,
user,
newTags,
newText,
commit,
changeGroupId,
form.getBonus(),
newPoll!=null?newPoll.getVariants():null,
form.isMultiselect(),
form.getEditorBonus()
);
if (changed || commit || publish) {
if (!newMsg.isDraft()) {
searchQueueSender.updateMessage(newMsg.getId(), commit || publish);
}
if (commit) {
feedPinger.pingFeedburner();
}
if (!publish || !preparedTopic.getSection().isPremoderated()) {
return new ModelAndView(new RedirectView(TopicLinkBuilder.baseLink(message).forceLastmod().build()));
} else {
params.put("moderated", true);
params.put("url", TopicLinkBuilder.baseLink(message).forceLastmod().build());
return new ModelAndView("add-done-moderated", params);
}
} else {
errors.reject(null, "Нет изменений");
}
}
params.put("newMsg", newMsg);
params.put(
"newPreparedMessage",
prepareService.prepareTopicPreview(
newMsg,
newTags!=null?TagService.namesToRefs(newTags):null,
newPoll,
request.isSecure(),
newText,
null
)
);
return new ModelAndView("edit", params);
}
private Poll buildNewPoll(Topic message, EditTopicRequest form) throws PollNotFoundException {
Poll poll = pollDao.getPollByTopicId(message.getId());
List<PollVariant> newVariants = new ArrayList<>();
for (PollVariant v : poll.getVariants()) {
String label = form.getPoll().get(v.getId());
if (!Strings.isNullOrEmpty(label)) {
newVariants.add(new PollVariant(v.getId(), label));
}
}
for (String label : form.getNewPoll()) {
if (!Strings.isNullOrEmpty(label)) {
newVariants.add(new PollVariant(0, label));
}
}
return poll.createNew(newVariants);
}
@InitBinder("form")
public void requestValidator(WebDataBinder binder) {
binder.setValidator(editTopicRequestValidator);
binder.setBindingErrorProcessor(new ExceptionBindingErrorProcessor());
}
}