1 16 package org.outerj.daisy.frontend.editor; 17 18 import org.outerj.daisy.frontend.util.AbstractDaisyApple; 19 import org.outerj.daisy.frontend.util.HttpMethodNotAllowedException; 20 import org.outerj.daisy.frontend.util.EncodingUtil; 21 import org.outerj.daisy.frontend.components.siteconf.SiteConf; 22 import org.outerj.daisy.frontend.WikiHelper; 23 import org.outerj.daisy.frontend.RequestUtil; 24 import org.outerj.daisy.frontend.PageContext; 25 import org.outerj.daisy.repository.*; 26 import org.outerj.daisy.repository.schema.*; 27 import org.outerj.daisy.navigation.NavigationManager; 28 import org.outerj.daisy.navigation.NavigationLookupResult; 29 import org.outerj.daisy.navigation.LookupAlternative; 30 import org.outerj.daisy.navigation.NavigationVersionMode; 31 import org.apache.avalon.framework.service.Serviceable; 32 import org.apache.avalon.framework.service.ServiceManager; 33 import org.apache.avalon.framework.service.ServiceException; 34 import org.apache.avalon.framework.context.Contextualizable; 35 import org.apache.avalon.framework.context.Context; 36 import org.apache.avalon.framework.context.ContextException; 37 import org.apache.avalon.framework.logger.LogEnabled; 38 import org.apache.avalon.framework.logger.Logger; 39 import org.apache.cocoon.components.flow.apples.AppleRequest; 40 import org.apache.cocoon.components.flow.apples.AppleResponse; 41 import org.apache.cocoon.environment.Request; 42 import org.apache.cocoon.ResourceNotFoundException; 43 import org.apache.cocoon.forms.formmodel.DataWidget; 44 import org.apache.cocoon.forms.datatype.Datatype; 45 import org.apache.cocoon.i18n.BundleFactory; 46 import org.apache.cocoon.i18n.Bundle; 47 48 import java.util.*; 49 import java.io.InputStream ; 50 import java.io.IOException ; 51 52 55 public class DocumentEditorApple extends AbstractDaisyApple implements Serviceable, Contextualizable, LogEnabled { 56 private ServiceManager serviceManager; 57 private boolean init = false; 58 private Repository repository; 59 private DocumentType documentType; 60 private Document document; 61 private String lastPathPart; 62 private String navigationPath; 63 private String currentPath; 64 private DocumentEditorForm form; 65 private Context context; 66 private SiteConf siteConf; 67 private Locale locale; 68 private Map viewData; 69 private String continuationId; 70 private boolean autoExtendLock = false; 71 private long lockExpires; 72 public static final int HEARTBEAT_INTERVAL = 10 * 60 * 1000; private Logger logger; 74 private String returnTo; 75 76 public void enableLogging(Logger logger) { 77 this.logger = logger; 78 } 79 80 public void service(ServiceManager serviceManager) throws ServiceException { 81 this.serviceManager = serviceManager; 82 } 83 84 public void contextualize(Context context) throws ContextException { 85 this.context = context; 86 this.continuationId = (String )context.get("continuation-id"); 87 } 88 89 protected void processInternal(AppleRequest appleRequest, AppleResponse appleResponse) throws Exception { 90 Request request = appleRequest.getCocoonRequest(); 91 92 if (!init) { 93 if (!request.getMethod().equals("POST") && !"true".equals(request.getParameter("startWithGet"))) { 96 throw new HttpMethodNotAllowedException(request.getMethod()); 97 } 98 99 repository = WikiHelper.getRepository(request, serviceManager); 100 lastPathPart = appleRequest.getSitemapParameter("lastPathPart"); 101 locale = WikiHelper.getLocale(request); 102 siteConf = WikiHelper.getSiteConf(request); 103 navigationPath = appleRequest.getSitemapParameter("navigationPath"); 104 String basePath = getMountPoint() + "/" + siteConf.getName(); 105 currentPath = basePath + "/" + navigationPath; 106 returnTo = request.getParameter("returnTo"); 108 init = true; 109 } 110 111 if (document == null) { 112 if (lastPathPart.equals("new")) { 113 Document document; 116 if (request.getParameter("documentType") != null) { 117 String documentType = RequestUtil.getStringParameter(request, "documentType"); 118 long branchId = RequestUtil.getBranchId(request, siteConf.getBranchId(), repository); 119 long languageId = RequestUtil.getLanguageId(request, siteConf.getLanguageId(), repository); 120 document = createNewDocument(documentType, branchId, languageId); 121 } else if (request.getParameter("template") != null) { 122 long template = RequestUtil.getLongParameter(request, "template"); 123 long branchId = RequestUtil.getBranchId(request, siteConf.getBranchId(), repository); 124 long languageId = RequestUtil.getLanguageId(request, siteConf.getLanguageId(), repository); 125 document = createNewDocumentFromTemplate(template, branchId, languageId); 126 } else if (request.getParameter("variantOf") != null) { 127 long documentId = RequestUtil.getLongParameter(request, "variantOf"); 128 long startBranchId = getBranch(request, "startBranch"); 129 long startLanguageId = getLanguage(request, "startLanguage"); 130 long newBranchId = getBranch(request, "newBranch"); 131 long newLanguageId = getLanguage(request, "newLanguage"); 132 document = createNewDocumentVariant(documentId, startBranchId, startLanguageId, newBranchId, newLanguageId); 133 } else if (request.getParameter("templateDocument") != null) { 134 String requestAttrName = RequestUtil.getStringParameter(request, "templateDocument"); 135 Object object = request.getAttribute(requestAttrName); 136 137 if (object == null) 138 throw new Exception ("Nothing found in request attribute specified in templateDocument parameter: " + requestAttrName); 139 if (!(object instanceof Document)) 140 throw new Exception ("Object specified in templateDocument parameter is of an incorrect type, got: " + object.getClass().getName()); 141 142 document = (Document)object; 143 documentType = repository.getRepositorySchema().getDocumentTypeById(document.getDocumentTypeId(), false); 144 } else { 145 throw new Exception ("Either template, documentType, variantOf or templateDocument parameter must be specified for the creation of a new document (variant)."); 146 } 147 this.document = document; 149 appleResponse.redirectTo(EncodingUtil.encodePath(getPath())); 150 return; 151 } else { 152 long requestedBranchId = RequestUtil.getBranchId(request, -1, repository); 153 long requestedLanguageId = RequestUtil.getLanguageId(request, -1, repository); 154 NavigationManager navigationManager = (NavigationManager)repository.getExtension("NavigationManager"); 155 NavigationVersionMode navVersionMode = WikiHelper.getVersionMode(request).getNavigationVersionMode(); 156 NavigationLookupResult lookupResult = navigationManager.lookup(navigationPath, requestedBranchId, requestedLanguageId, 157 new LookupAlternative[] { new LookupAlternative(siteConf.getName(), siteConf.getCollectionId(), siteConf.getNavigationDoc(), navVersionMode)}); 158 if (lookupResult.isNotFound()) { 159 throw new ResourceNotFoundException("Path not found: " + navigationPath); 160 } else if (lookupResult.isRedirect() && lookupResult.getVariantKey() == null) { 161 throw new Exception ("Can't handle redirect navigation nodes."); 162 } 163 164 VariantKey variantKey = lookupResult.getVariantKey(); 165 Document document = repository.getDocument(variantKey, true); 166 documentType = repository.getRepositorySchema().getDocumentTypeById(document.getDocumentTypeId(), false); 167 168 boolean showLockWarnPage = false; 169 LockInfo lockInfo = document.getLockInfo(false); 170 if (lockInfo.hasLock() && lockInfo.getUserId() != repository.getUserId()) { 171 showLockWarnPage = true; 172 } else if (siteConf.getAutomaticLocking()) { 173 boolean success = document.lock(siteConf.getDefaultLockTime(), siteConf.getLockType()); 174 lockInfo = document.getLockInfo(false); 175 if (!success) { 176 showLockWarnPage = true; 177 } else { 178 autoExtendLock = siteConf.getAutoExtendLock(); 179 lockExpires = lockInfo.getTimeAcquired().getTime() + lockInfo.getDuration(); 180 document = repository.getDocument(variantKey, true); 185 } 186 } 187 188 if (request.getParameter("versionId") != null) { 190 long versionId = RequestUtil.getLongParameter(request, "versionId"); 191 if (versionId > document.getLastVersionId() || versionId < 1) { 192 throw new Exception ("Specified version ID is out of range: " + versionId); 193 } 194 revertDocument(document, versionId); 195 } 196 197 this.document = document; 199 200 if (showLockWarnPage) { 201 Map viewData = new HashMap(); 202 viewData.put("lockInfo", lockInfo); 203 String userName = repository.getUserManager().getUserDisplayName(lockInfo.getUserId()); 204 viewData.put("lockUserName", userName); 205 viewData.put("pageContext", new PageContext(getMountPoint(), siteConf, repository, getLayoutType(), getSkin(), getContext())); 206 viewData.put("editPath", getPath()); 207 viewData.put("documentPath", currentPath + ".html" + getBranchLangQueryString()); 208 209 appleResponse.sendPage("Message-locked-Pipe", viewData); 210 return; 211 } else { 212 appleResponse.redirectTo(EncodingUtil.encodePath(getPath())); 213 return; 214 } 215 } 216 } 217 218 if (form == null) { 219 final DocumentEditorForm form = DocumentEditorFormBuilder.build(documentType, document.getBranchId(), 220 document.getLanguageId(), serviceManager, context, locale, repository, logger); 221 222 viewData = new HashMap(); 223 viewData.put("submitPath", getPath()); 224 viewData.put("collectionsArray", repository.getCollectionManager().getCollections(false).getArray()); 225 viewData.put("pageContext", new PageContext(getMountPoint(), siteConf, repository, getLayoutType("mini"), getSkin(), getContext())); 226 viewData.put("locale", locale); 227 viewData.put("htmlareaLang", locale.getLanguage()); 228 viewData.put("hasEditors", Boolean.valueOf(hasEditors())); 229 viewData.put("documentEditorForm", form); 230 viewData.put("document", document); 231 232 DocumentBinding.load(form, document, repository, locale); 233 234 if (!document.isVariantNew() && document.getLastVersion().getState() == VersionState.DRAFT) { 236 form.setPublishImmediately(false); 237 } else if (siteConf.getNewVersionStateDefault() == VersionState.PUBLISH) { 238 form.setPublishImmediately(true); 239 } 240 241 this.form = form; 244 } 245 246 String resource = appleRequest.getSitemapParameter("resource"); 247 if ("heartbeat".equals(resource)) { 248 LockInfo lockInfo = null; 249 if (autoExtendLock) { 250 int margin = 3 * 60 * 1000; if (lockExpires - System.currentTimeMillis() - HEARTBEAT_INTERVAL - margin < 0) { 252 boolean success = document.lock(siteConf.getDefaultLockTime(), siteConf.getLockType()); 254 if (!success) { 255 lockInfo = document.getLockInfo(false); 256 } 257 } 258 } 259 HashMap viewData = new HashMap(); 260 viewData.put("heartbeatInterval", String.valueOf(HEARTBEAT_INTERVAL)); 261 if (lockInfo != null) { 262 viewData.put("lockInfo", lockInfo); 263 String userName = repository.getUserManager().getUserDisplayName(lockInfo.getUserId()); 264 viewData.put("lockUserName", userName); 265 } 266 appleResponse.sendPage("Template-heartbeat-Pipe", viewData); 267 } else if ("selectionList".equals(resource)) { 268 handleSelectionListRequest(request, appleResponse); 269 } else if (resource == null) { 270 appleResponse.redirectTo(EncodingUtil.encodePath(getPath() + "/" + form.getActiveFormName())); 271 } else { 272 String method = request.getMethod(); 273 if (method.equals("GET")) { 274 form.setActiveForm(resource); 275 appleResponse.sendPage("EditDocumentPipe", getEditDocumentViewData()); 277 } else if (method.equals("POST")) { 278 if ("true".equals(request.getParameter("cancelEditing"))) { 279 document.releaseLock(); 280 if (returnTo != null) 281 appleResponse.redirectTo(returnTo); 282 else if (document.getId() != -1) 283 appleResponse.redirectTo(EncodingUtil.encodePathQuery(currentPath + "/../" + document.getId() + ".html" + (!document.isVariantNew() ? getBranchLangQueryString() : ""))); 284 else 285 appleResponse.redirectTo(EncodingUtil.encodePath(getMountPoint() + "/" + siteConf.getName() + "/")); 286 } else { 287 boolean endProcessing = form.process(request, locale, resource); 288 289 if (endProcessing) { 290 DocumentBinding.save(form, document, repository); 291 document.save(form.getValidateOnSave()); 292 if (form.getPublishImmediately()) { 293 Version lastVersion = document.getLastVersion(); 294 if (lastVersion.getState() != VersionState.PUBLISH) { 295 try { 296 lastVersion.setState(VersionState.PUBLISH); 297 } catch (AccessException e) { 298 } 300 } 301 } 302 document.releaseLock(); 303 if (returnTo != null) 304 appleResponse.redirectTo(returnTo); 305 else 306 appleResponse.redirectTo(EncodingUtil.encodePathQuery(currentPath + "/../" + document.getId() + ".html" + getBranchLangQueryString())); 307 } else { 308 if (!form.getActiveFormName().equals(resource)) { 309 appleResponse.redirectTo(EncodingUtil.encodePath(getPath() + "/" + form.getActiveFormName())); 310 } else { 311 appleResponse.sendPage("EditDocumentPipe", getEditDocumentViewData()); 312 } 313 } 314 } 315 } else { 316 throw new HttpMethodNotAllowedException(method); 317 } 318 } 319 320 } 321 322 private Map getEditDocumentViewData() { 323 Map viewData = new HashMap(this.viewData); 324 viewData.put("CocoonFormsInstance", form.getActiveForm()); 325 viewData.put("activeFormName", form.getActiveFormName()); 326 viewData.put("activeFormTemplate", form.getActiveFormTemplate()); 327 return viewData; 328 } 329 330 private String getBranchLangQueryString() throws RepositoryException { 331 if (document.getBranchId() != siteConf.getBranchId() || document.getLanguageId() != siteConf.getLanguageId()) { 332 String branch = repository.getVariantManager().getBranch(document.getBranchId(), false).getName(); 333 String language = repository.getVariantManager().getLanguage(document.getLanguageId(), false).getName(); 334 return "?branch=" + branch + "&language=" + language; 335 } else { 336 return ""; 337 } 338 } 339 340 343 private String getNewDocumentName() throws Exception { 344 Bundle bundle = null; 345 BundleFactory bundleFactory = (BundleFactory)serviceManager.lookup(BundleFactory.ROLE); 346 try { 347 bundle = bundleFactory.select("resources/i18n", "messages", locale); 348 return bundle.getString("editdoc.new-document-name"); 349 } finally { 350 if (bundle != null) 351 bundleFactory.release(bundle); 352 serviceManager.release(bundleFactory); 353 } 354 } 355 356 private Document createNewDocument(String documentTypeName, long branchId, long languageId) throws Exception { 357 documentType = repository.getRepositorySchema().getDocumentType(documentTypeName, false); 358 Document document = repository.createDocument(getNewDocumentName(), documentType.getId(), branchId, languageId); 359 DocumentCollection collection = repository.getCollectionManager().getCollection(siteConf.getCollectionId(), false); 360 document.addToCollection(collection); 361 return document; 362 } 363 364 private Document createNewDocumentFromTemplate(long templateDocumentId, long branchId, long languageId) throws Exception { 365 Document templateDoc = repository.getDocument(templateDocumentId, branchId, languageId, false); 366 367 Document document = repository.createDocument(getNewDocumentName(), templateDoc.getDocumentTypeId(), branchId, languageId); 368 this.documentType = repository.getRepositorySchema().getDocumentTypeById(templateDoc.getDocumentTypeId(), false); 369 370 org.outerj.daisy.repository.Field[] fields = templateDoc.getFields().getArray(); 372 for (int i = 0; i < fields.length; i++) { 373 if (documentType.hasFieldType(fields[i].getTypeId())) 376 document.setField(fields[i].getTypeId(), fields[i].getValue()); 377 } 378 379 Part[] parts = templateDoc.getParts().getArray(); 380 for (int i = 0; i < parts.length; i++) { 381 if (documentType.hasPartType(parts[i].getTypeId())) 382 document.setPart(parts[i].getTypeId(), parts[i].getMimeType(), new PartPartDataSource(parts[i])); 383 } 384 385 Link[] links = templateDoc.getLinks().getArray(); 386 for (int i = 0; i < links.length; i++) 387 document.addLink(links[i].getTitle(), links[i].getTarget()); 388 389 DocumentCollection[] collections = templateDoc.getCollections().getArray(); 390 for (int i = 0; i < collections.length; i++) 391 document.addToCollection(collections[i]); 392 393 Iterator customFieldIt = templateDoc.getCustomFields().entrySet().iterator(); 394 while (customFieldIt.hasNext()) { 395 Map.Entry entry = (Map.Entry)customFieldIt.next(); 396 document.setCustomField((String )entry.getKey(), (String )entry.getValue()); 397 } 398 399 return document; 400 } 401 402 private Document createNewDocumentVariant(long documentId, long startBranchId, long startLanguageId, 403 long newBranchId, long newLanguageId) throws Exception { 404 Document document = repository.createVariant(documentId, startBranchId, startLanguageId, -1, newBranchId, newLanguageId, false); 405 documentType = repository.getRepositorySchema().getDocumentTypeById(document.getDocumentTypeId(), false); 406 return document; 407 } 408 409 412 private boolean hasEditors() { 413 PartTypeUse[] partTypeUses = documentType.getPartTypeUses(); 414 for (int i = 0; i < partTypeUses.length; i++) { 415 if (partTypeUses[i].getPartType().isDaisyHtml()) 416 return true; 417 } 418 return false; 419 } 420 421 private String getPath() { 422 return currentPath + "/edit/" + continuationId; 423 } 424 425 static class PartPartDataSource implements PartDataSource { 426 private Part part; 427 428 public PartPartDataSource(Part part) { 429 this.part = part; 430 } 431 432 public InputStream createInputStream() throws IOException , RepositoryException { 433 return part.getDataStream(); 434 } 435 436 public long getSize() { 437 return part.getSize(); 438 } 439 } 440 441 private void handleSelectionListRequest(Request request, AppleResponse response) throws Exception { 442 String widgetPath = RequestUtil.getStringParameter(request, "widgetPath"); 443 DataWidget widget = (DataWidget)form.getFieldsForm().lookupWidget(widgetPath.replace('.', '/')); 444 Datatype datatype = widget.getDatatype(); 445 446 long fieldTypeId = RequestUtil.getLongParameter(request, "fieldTypeId"); 447 FieldTypeUse fieldTypeUse = documentType.getFieldTypeUse(fieldTypeId); 448 if (fieldTypeUse == null) 449 throw new Exception ("Document type does not have a field type with ID " + fieldTypeId); 450 FieldType fieldType = fieldTypeUse.getFieldType(); 451 452 SelectionList selectionList = fieldType.getSelectionList(); 453 if (selectionList == null) 454 throw new Exception ("Field type with id " + fieldTypeId + " does not have a selection list."); 455 456 SelectionListAdapter selectionListAdapter = new SelectionListAdapter(datatype, selectionList, false, 457 fieldType.getValueType(), repository.getVariantManager(), document.getBranchId(), document.getLanguageId()); 458 Map viewData = new HashMap(); 459 viewData.put("selectionListAdapter", selectionListAdapter); 460 viewData.put("widgetPath", widgetPath); 461 viewData.put("fieldTypeLabel", fieldType.getLabel(locale)); 462 viewData.put("locale", locale); 463 response.sendPage("SelectionListPipe", viewData); 464 } 465 466 470 private long getBranch(Request request, String baseName) throws Exception { 471 String paramName = baseName; 472 String idName = baseName + "Id"; 473 if (request.getParameter(idName) != null) 474 paramName = idName; 475 else if (request.getParameter(paramName) == null) 476 throw new Exception ("Missing request parameter: " + baseName + " or " + idName); 477 478 return RequestUtil.getBranchId(request.getParameter(paramName), -1, repository); 479 } 480 481 485 private long getLanguage(Request request, String baseName) throws Exception { 486 String paramName = baseName; 487 String idName = baseName + "Id"; 488 if (request.getParameter(idName) != null) 489 paramName = idName; 490 else if (request.getParameter(paramName) == null) 491 throw new Exception ("Missing request parameter: " + baseName + " or " + idName); 492 493 return RequestUtil.getLanguageId(request.getParameter(paramName), -1, repository); 494 } 495 496 499 private void revertDocument(Document document, long versionId) throws Exception { 500 try { 506 Version version = document.getVersion(versionId); 507 508 Part[] oldParts = document.getParts().getArray(); 511 for (int i = 0; i < oldParts.length; i++) { 512 document.deletePart(oldParts[i].getTypeId()); 513 } 514 Part[] parts = version.getParts().getArray(); 516 for (int i = 0; i < parts.length; i++) { 517 document.setPart(parts[i].getTypeId(), parts[i].getMimeType(), new PartPartDataSource(parts[i])); 518 document.setPartFileName(parts[i].getTypeId(), parts[i].getFileName()); 519 } 520 521 Field[] oldFields = document.getFields().getArray(); 524 for (int i = 0; i < oldFields.length; i++) { 525 document.deleteField(oldFields[i].getTypeId()); 526 } 527 Field[] fields = version.getFields().getArray(); 529 for (int i = 0; i < fields.length; i++) { 530 document.setField(fields[i].getTypeId(), fields[i].getValue()); 531 } 532 533 document.setName(version.getDocumentName()); 535 536 document.clearLinks(); 538 Link[] links = version.getLinks().getArray(); 539 for (int i = 0; i < links.length; i++) { 540 document.addLink(links[i].getTitle(), links[i].getTarget()); 541 } 542 } catch (DocumentTypeInconsistencyException e) { 543 throw new Exception ("Failed to revert to version " + versionId + " because of changes in the document type.", e); 544 } catch (RepositoryException e) { 545 throw new Exception ("Error while reverting document content to version " + versionId + ".", e); 546 } 547 } 548 } 549 | Popular Tags |