001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.ui.dialogs; 029 030import org.opencms.configuration.CmsSchedulerConfiguration; 031import org.opencms.db.CmsResourceState; 032import org.opencms.file.CmsObject; 033import org.opencms.file.CmsProject; 034import org.opencms.file.CmsResource; 035import org.opencms.file.CmsResourceFilter; 036import org.opencms.lock.CmsLock; 037import org.opencms.main.CmsContextInfo; 038import org.opencms.main.CmsException; 039import org.opencms.main.CmsLog; 040import org.opencms.main.OpenCms; 041import org.opencms.scheduler.CmsScheduledJobInfo; 042import org.opencms.scheduler.jobs.CmsPublishScheduledJob; 043import org.opencms.security.CmsPermissionSet; 044import org.opencms.security.CmsRole; 045import org.opencms.ui.CmsVaadinUtils; 046import org.opencms.ui.I_CmsDialogContext; 047import org.opencms.ui.components.CmsBasicDialog; 048import org.opencms.ui.components.CmsDateField; 049import org.opencms.ui.components.CmsOkCancelActionHandler; 050import org.opencms.util.CmsDateUtil; 051import org.opencms.util.CmsUUID; 052import org.opencms.workplace.CmsWorkplaceAction; 053import org.opencms.workplace.commons.Messages; 054 055import java.text.DateFormat; 056import java.time.LocalDateTime; 057import java.util.Calendar; 058import java.util.Collections; 059import java.util.Date; 060import java.util.HashSet; 061import java.util.List; 062import java.util.Set; 063import java.util.SortedMap; 064import java.util.TreeMap; 065 066import org.apache.commons.logging.Log; 067 068import com.vaadin.ui.Button; 069import com.vaadin.ui.Button.ClickEvent; 070import com.vaadin.ui.Button.ClickListener; 071import com.vaadin.ui.CheckBox; 072import com.vaadin.ui.FormLayout; 073 074/** 075 * The publish schedule dialog.<p> 076 */ 077public class CmsPublishScheduledDialog extends CmsBasicDialog { 078 079 /** The serial version id. */ 080 private static final long serialVersionUID = 7488454443783670970L; 081 082 /** Logger instance for this class. */ 083 private static final Log LOG = CmsLog.getLog(CmsPublishScheduledDialog.class); 084 085 /** The dialog context. */ 086 private I_CmsDialogContext m_context; 087 088 /** The date selection field. */ 089 private CmsDateField m_dateField; 090 091 /** The OK button. */ 092 private Button m_okButton; 093 094 /** The cancel button. */ 095 private Button m_cancelButton; 096 097 /** Include sub-resources check box. */ 098 private CheckBox m_includeSubResources; 099 100 /** 101 * Constructor.<p> 102 * 103 * @param context the dialog context 104 */ 105 public CmsPublishScheduledDialog(I_CmsDialogContext context) { 106 107 m_context = context; 108 displayResourceInfo(context.getResources()); 109 FormLayout form = initForm(); 110 setContent(form); 111 m_okButton = new Button(CmsVaadinUtils.getMessageText(org.opencms.workplace.Messages.GUI_DIALOG_BUTTON_OK_0)); 112 m_okButton.addClickListener(new ClickListener() { 113 114 private static final long serialVersionUID = 1L; 115 116 public void buttonClick(ClickEvent event) { 117 118 submit(); 119 } 120 }); 121 addButton(m_okButton); 122 m_cancelButton = new Button( 123 CmsVaadinUtils.getMessageText(org.opencms.workplace.Messages.GUI_DIALOG_BUTTON_CANCEL_0)); 124 m_cancelButton.addClickListener(new ClickListener() { 125 126 private static final long serialVersionUID = 1L; 127 128 public void buttonClick(ClickEvent event) { 129 130 cancel(); 131 } 132 }); 133 addButton(m_cancelButton); 134 135 setActionHandler(new CmsOkCancelActionHandler() { 136 137 private static final long serialVersionUID = 1L; 138 139 @Override 140 protected void cancel() { 141 142 CmsPublishScheduledDialog.this.cancel(); 143 } 144 145 @Override 146 protected void ok() { 147 148 submit(); 149 } 150 }); 151 152 m_dateField.setRangeStart(LocalDateTime.now()); 153 154 } 155 156 /** 157 * Cancels the dialog action.<p> 158 */ 159 void cancel() { 160 161 m_context.finish(Collections.<CmsUUID> emptyList()); 162 } 163 164 /** 165 * Submits the dialog action.<p> 166 */ 167 void submit() { 168 169 // if (!m_dateField.isValid()) { 170 // return; 171 // } 172 long current = System.currentTimeMillis(); 173 Date dateValue = m_dateField.getDate(); 174 long publishTime = dateValue.getTime(); 175 if (current > publishTime) { 176 m_context.error( 177 new CmsException(Messages.get().container(Messages.ERR_PUBLISH_SCHEDULED_DATE_IN_PAST_1, dateValue))); 178 } else { 179 try { 180 // make copies from the admin cmsobject and the user cmsobject 181 // get the admin cms object 182 CmsWorkplaceAction action = CmsWorkplaceAction.getInstance(); 183 CmsObject cmsAdmin = action.getCmsAdminObject(); 184 // get the user cms object 185 CmsObject cms = OpenCms.initCmsObject(m_context.getCms()); 186 187 // set the current user site to the admin cms object 188 cmsAdmin.getRequestContext().setSiteRoot(cms.getRequestContext().getSiteRoot()); 189 CmsProject tmpProject = createTempProject(cmsAdmin, m_context.getResources(), dateValue); 190 // set project as current project 191 cmsAdmin.getRequestContext().setCurrentProject(tmpProject); 192 cms.getRequestContext().setCurrentProject(tmpProject); 193 194 Set<CmsUUID> changeIds = new HashSet<CmsUUID>(); 195 for (CmsResource resource : m_context.getResources()) { 196 addToTempProject(cmsAdmin, cms, resource, tmpProject); 197 if (resource.isFolder() && m_includeSubResources.getValue().booleanValue()) { 198 List<CmsResource> subResources = cms.readResources( 199 resource, 200 CmsResourceFilter.ONLY_VISIBLE.addExcludeState(CmsResourceState.STATE_UNCHANGED), 201 true); 202 for (CmsResource sub : subResources) { 203 // check publish permissions on sub resource 204 if (cms.hasPermissions( 205 sub, 206 CmsPermissionSet.ACCESS_DIRECT_PUBLISH, 207 false, 208 CmsResourceFilter.ALL)) { 209 addToTempProject(cmsAdmin, cms, sub, tmpProject); 210 } 211 } 212 } 213 214 changeIds.add(resource.getStructureId()); 215 } 216 // create a new scheduled job 217 CmsScheduledJobInfo job = new CmsScheduledJobInfo(); 218 // the job name 219 String jobName = tmpProject.getName(); 220 jobName = jobName.replace("/", "/"); 221 // set the job parameters 222 job.setJobName(jobName); 223 job.setClassName("org.opencms.scheduler.jobs.CmsPublishScheduledJob"); 224 // create the cron expression 225 Calendar calendar = Calendar.getInstance(); 226 calendar.setTime(dateValue); 227 String cronExpr = "" 228 + calendar.get(Calendar.SECOND) 229 + " " 230 + calendar.get(Calendar.MINUTE) 231 + " " 232 + calendar.get(Calendar.HOUR_OF_DAY) 233 + " " 234 + calendar.get(Calendar.DAY_OF_MONTH) 235 + " " 236 + (calendar.get(Calendar.MONTH) + 1) 237 + " " 238 + "?" 239 + " " 240 + calendar.get(Calendar.YEAR); 241 // set the cron expression 242 job.setCronExpression(cronExpr); 243 // set the job active 244 job.setActive(true); 245 // create the context info 246 CmsContextInfo contextInfo = new CmsContextInfo(); 247 contextInfo.setProjectName(tmpProject.getName()); 248 contextInfo.setUserName(cmsAdmin.getRequestContext().getCurrentUser().getName()); 249 // create the job schedule parameter 250 SortedMap<String, String> params = new TreeMap<String, String>(); 251 // the user to send mail to 252 String userName = m_context.getCms().getRequestContext().getCurrentUser().getName(); 253 params.put(CmsPublishScheduledJob.PARAM_USER, userName); 254 // the job name 255 params.put(CmsPublishScheduledJob.PARAM_JOBNAME, jobName); 256 // the link check 257 params.put(CmsPublishScheduledJob.PARAM_LINKCHECK, "true"); 258 // add the job schedule parameter 259 job.setParameters(params); 260 // add the context info to the scheduled job 261 job.setContextInfo(contextInfo); 262 // add the job to the scheduled job list 263 OpenCms.getScheduleManager().scheduleJob(cmsAdmin, job); 264 // update the XML configuration 265 OpenCms.writeConfiguration(CmsSchedulerConfiguration.class); 266 m_context.finish(changeIds); 267 } catch (CmsException ex) { 268 LOG.error("Error performing publish scheduled dialog operation.", ex); 269 m_context.error(ex); 270 } 271 } 272 273 } 274 275 /** 276 * Adds the given resource to the temporary project.<p> 277 * 278 * @param adminCms the admin cms context 279 * @param userCms the user cms context 280 * @param resource the resource 281 * @param tmpProject the temporary project 282 * @throws CmsException in case something goes wrong 283 */ 284 private void addToTempProject(CmsObject adminCms, CmsObject userCms, CmsResource resource, CmsProject tmpProject) 285 throws CmsException { 286 287 // copy the resource to the project 288 adminCms.copyResourceToProject(resource); 289 290 // lock the resource in the current project 291 CmsLock lock = userCms.getLock(resource); 292 // prove is current lock from current but not in current project 293 if ((lock != null) 294 && lock.isOwnedBy(userCms.getRequestContext().getCurrentUser()) 295 && !lock.isOwnedInProjectBy( 296 userCms.getRequestContext().getCurrentUser(), 297 userCms.getRequestContext().getCurrentProject())) { 298 // file is locked by current user but not in current project 299 // change the lock from this file 300 userCms.changeLock(resource); 301 } 302 // lock resource from current user in current project 303 userCms.lockResource(resource); 304 // get current lock 305 lock = userCms.getLock(resource); 306 } 307 308 /** 309 * Creates the publish project's name for a given root path and publish date.<p> 310 * 311 * @param rootPath the publish resource's root path 312 * @param date the publish date 313 * 314 * @return the publish project name 315 */ 316 private String computeProjectName(String rootPath, Date date) { 317 318 // create the temporary project, which is deleted after publishing 319 // the publish scheduled date in project name 320 String dateTime = CmsDateUtil.getDateTime(date, DateFormat.SHORT, getLocale()); 321 // the resource name to publish scheduled 322 String projectName = CmsVaadinUtils.getMessageText( 323 Messages.GUI_PUBLISH_SCHEDULED_PROJECT_NAME_2, 324 rootPath, 325 dateTime); 326 // the HTML encoding for slashes is necessary because of the slashes in english date time format 327 // in project names slahes are not allowed, because these are separators for organizaional units 328 projectName = projectName.replace("/", "/"); 329 while (projectName.length() > 190) { 330 rootPath = "..." + rootPath.substring(5, rootPath.length()); 331 projectName = computeProjectName(rootPath, date); 332 } 333 return projectName; 334 } 335 336 /** 337 * Creates the temporary project.<p> 338 * 339 * @param adminCms the admin cms context 340 * @param resources the resources 341 * @param date the publish date 342 * 343 * @return the project 344 * 345 * @throws CmsException in case writing the project fails 346 */ 347 private CmsProject createTempProject(CmsObject adminCms, List<CmsResource> resources, Date date) 348 throws CmsException { 349 350 CmsProject tmpProject; 351 352 String rootPath = resources.get(0).getRootPath(); 353 if (resources.size() > 1) { 354 rootPath = CmsResource.getParentFolder(rootPath); 355 } 356 String projectName = computeProjectName(rootPath, date); 357 358 try { 359 // create the project 360 tmpProject = adminCms.createProject( 361 projectName, 362 "", 363 CmsRole.WORKPLACE_USER.getGroupName(), 364 CmsRole.PROJECT_MANAGER.getGroupName(), 365 CmsProject.PROJECT_TYPE_TEMPORARY); 366 } catch (CmsException e) { 367 String resName = CmsResource.getName(rootPath); 368 if (resName.length() > 64) { 369 resName = resName.substring(0, 64) + "..."; 370 } 371 // use UUID to make sure the project name is still unique 372 projectName = computeProjectName(resName, date) + " [" + new CmsUUID() + "]"; 373 // create the project 374 tmpProject = adminCms.createProject( 375 projectName, 376 "", 377 CmsRole.WORKPLACE_USER.getGroupName(), 378 CmsRole.PROJECT_MANAGER.getGroupName(), 379 CmsProject.PROJECT_TYPE_TEMPORARY); 380 } 381 // make the project invisible for all users 382 tmpProject.setHidden(true); 383 // write the project to the database 384 adminCms.writeProject(tmpProject); 385 return tmpProject; 386 } 387 388 /** 389 * Checks whether the resources list contains any folders.<p> 390 * 391 * @return <code>true</code> if the resources list contains any folders 392 */ 393 private boolean hasFolders() { 394 395 for (CmsResource resource : m_context.getResources()) { 396 if (resource.isFolder()) { 397 return true; 398 } 399 } 400 return false; 401 } 402 403 /** 404 * Initializes the form fields.<p> 405 * 406 * @return the form component 407 */ 408 private FormLayout initForm() { 409 410 FormLayout form = new FormLayout(); 411 form.setWidth("100%"); 412 m_dateField = new CmsDateField(); 413 m_dateField.setCaption( 414 CmsVaadinUtils.getMessageText(org.opencms.workplace.commons.Messages.GUI_LABEL_DATE_PUBLISH_SCHEDULED_0)); 415 form.addComponent(m_dateField); 416 m_includeSubResources = new CheckBox( 417 CmsVaadinUtils.getMessageText(org.opencms.workplace.commons.Messages.GUI_PUBLISH_MULTI_SUBRESOURCES_0)); 418 if (hasFolders()) { 419 form.addComponent(m_includeSubResources); 420 } 421 422 return form; 423 } 424}