001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (C) Alkacon Software (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.gwt; 029 030import org.opencms.ade.containerpage.CmsRelationTargetListBean; 031import org.opencms.file.CmsFile; 032import org.opencms.file.CmsObject; 033import org.opencms.file.CmsProperty; 034import org.opencms.file.CmsPropertyDefinition; 035import org.opencms.file.CmsResource; 036import org.opencms.file.CmsResourceFilter; 037import org.opencms.file.CmsUser; 038import org.opencms.file.CmsVfsResourceNotFoundException; 039import org.opencms.file.types.CmsResourceTypeJsp; 040import org.opencms.file.types.CmsResourceTypeXmlContainerPage; 041import org.opencms.file.types.CmsResourceTypeXmlContent; 042import org.opencms.file.types.I_CmsResourceType; 043import org.opencms.gwt.shared.CmsListInfoBean; 044import org.opencms.gwt.shared.CmsPermissionInfo; 045import org.opencms.gwt.shared.CmsResourceStatusBean; 046import org.opencms.gwt.shared.CmsResourceStatusRelationBean; 047import org.opencms.gwt.shared.CmsResourceStatusTabId; 048import org.opencms.i18n.CmsLocaleManager; 049import org.opencms.i18n.CmsMessageContainer; 050import org.opencms.jsp.CmsJspTagContainer; 051import org.opencms.lock.CmsLock; 052import org.opencms.main.CmsException; 053import org.opencms.main.CmsLog; 054import org.opencms.main.OpenCms; 055import org.opencms.relations.CmsRelation; 056import org.opencms.relations.CmsRelationFilter; 057import org.opencms.relations.CmsRelationType; 058import org.opencms.relations.I_CmsLinkParseable; 059import org.opencms.search.galleries.CmsGallerySearch; 060import org.opencms.search.galleries.CmsGallerySearchResult; 061import org.opencms.security.CmsRole; 062import org.opencms.site.CmsSite; 063import org.opencms.util.CmsStringUtil; 064import org.opencms.util.CmsUUID; 065import org.opencms.workplace.CmsWorkplace; 066import org.opencms.workplace.explorer.CmsResourceUtil; 067import org.opencms.xml.content.CmsXmlContent; 068import org.opencms.xml.content.CmsXmlContentFactory; 069 070import java.util.ArrayList; 071import java.util.Arrays; 072import java.util.Collections; 073import java.util.Comparator; 074import java.util.HashMap; 075import java.util.Iterator; 076import java.util.LinkedHashMap; 077import java.util.List; 078import java.util.Locale; 079import java.util.Map; 080import java.util.Set; 081 082import org.apache.commons.logging.Log; 083 084import com.google.common.base.Optional; 085import com.google.common.collect.ComparisonChain; 086import com.google.common.collect.Maps; 087import com.google.common.collect.Sets; 088 089/** 090 * Helper class to generate all the data which is necessary for the resource status dialog(s).<p> 091 */ 092public class CmsDefaultResourceStatusProvider { 093 094 /** The log instance for this class. */ 095 private static final Log LOG = CmsLog.getLog(CmsDefaultResourceStatusProvider.class); 096 097 /** 098 * Gets the relation targets for a resource.<p> 099 * 100 * @param cms the current CMS context 101 * @param source the structure id of the resource for which we want the relation targets 102 * @param additionalIds the structure ids of additional resources to include with the relation targets 103 * @param cancelIfChanged if this is true, this method will stop immediately if it finds a changed resource among the relation targets 104 * 105 * @return a bean containing a list of relation targets 106 * 107 * @throws CmsException if something goes wrong 108 */ 109 public static CmsRelationTargetListBean getContainerpageRelationTargets( 110 CmsObject cms, 111 CmsUUID source, 112 List<CmsUUID> additionalIds, 113 boolean cancelIfChanged) 114 throws CmsException { 115 116 CmsRelationTargetListBean result = new CmsRelationTargetListBean(); 117 CmsResource content = cms.readResource(source, CmsResourceFilter.ALL); 118 boolean isContainerPage = CmsResourceTypeXmlContainerPage.isContainerPage(content); 119 if (additionalIds != null) { 120 for (CmsUUID structureId : additionalIds) { 121 try { 122 CmsResource res = cms.readResource(structureId, CmsResourceFilter.ALL); 123 result.add(res); 124 if (res.getState().isChanged() && cancelIfChanged) { 125 return result; 126 } 127 } catch (CmsException e) { 128 LOG.error(e.getLocalizedMessage(), e); 129 } 130 } 131 } 132 List<CmsRelation> relations = cms.readRelations(CmsRelationFilter.relationsFromStructureId(source)); 133 for (CmsRelation relation : relations) { 134 if (relation.getType() == CmsRelationType.XSD) { 135 continue; 136 } 137 try { 138 CmsResource target = relation.getTarget(cms, CmsResourceFilter.ALL); 139 I_CmsResourceType type = OpenCms.getResourceManager().getResourceType(target); 140 if (isContainerPage && (type instanceof CmsResourceTypeJsp)) { 141 // ignore formatters for container pages, as the normal user probably doesn't want to deal with them 142 continue; 143 } 144 result.add(target); 145 if (target.getState().isChanged() && cancelIfChanged) { 146 return result; 147 } 148 } catch (CmsException e) { 149 LOG.debug(e.getLocalizedMessage(), e); 150 } 151 } 152 return result; 153 } 154 155 /** 156 * Collects all the data to display in the resource status dialog.<p> 157 * 158 * @param cms the current CMS context 159 * @param structureId the structure id of the resource for which we want the information 160 * @param contentLocale the content locale 161 * @param includeTargets true if relation targets should be included 162 * @param detailContentId the structure id of the detail content if present 163 * @param additionalStructureIds structure ids of additional resources to include with the relation targets 164 * 165 * @return the resource status information 166 * @throws CmsException if something goes wrong 167 */ 168 public CmsResourceStatusBean getResourceStatus( 169 CmsObject cms, 170 CmsUUID structureId, 171 String contentLocale, 172 boolean includeTargets, 173 CmsUUID detailContentId, 174 List<CmsUUID> additionalStructureIds) 175 throws CmsException { 176 177 Locale locale = OpenCms.getWorkplaceManager().getWorkplaceLocale(cms); 178 cms.getRequestContext().setLocale(locale); 179 CmsResource resource = cms.readResource(structureId, CmsResourceFilter.ALL); 180 String localizedTitle = null; 181 Locale realLocale = null; 182 if (!CmsStringUtil.isEmptyOrWhitespaceOnly(contentLocale)) { 183 realLocale = CmsLocaleManager.getLocale(contentLocale); 184 CmsGallerySearchResult result = CmsGallerySearch.searchById(cms, structureId, realLocale); 185 if (!CmsStringUtil.isEmptyOrWhitespaceOnly(result.getTitle())) { 186 localizedTitle = result.getTitle(); 187 } 188 } 189 CmsResourceUtil resourceUtil = new CmsResourceUtil(cms, resource); 190 List<CmsProperty> properties = cms.readPropertyObjects(resource, false); 191 CmsResourceStatusBean result = new CmsResourceStatusBean(); 192 result.setDateCreated(CmsVfsService.formatDateTime(cms, resource.getDateCreated())); 193 long dateExpired = resource.getDateExpired(); 194 if (dateExpired != CmsResource.DATE_EXPIRED_DEFAULT) { 195 result.setDateExpired(CmsVfsService.formatDateTime(cms, dateExpired)); 196 } 197 result.setDateLastModified(CmsVfsService.formatDateTime(cms, resource.getDateLastModified())); 198 long dateReleased = resource.getDateReleased(); 199 if (dateReleased != CmsResource.DATE_RELEASED_DEFAULT) { 200 result.setDateReleased(CmsVfsService.formatDateTime(cms, dateReleased)); 201 } 202 String lastProject = resourceUtil.getLockedInProjectName(); 203 if ("".equals(lastProject)) { 204 lastProject = null; 205 } 206 result.setLastProject(lastProject); 207 208 result.setListInfo(CmsVfsService.getPageInfo(cms, resource)); 209 CmsLock lock = cms.getLock(resource); 210 CmsUser lockOwner = null; 211 if (!lock.isUnlocked()) { 212 lockOwner = cms.readUser(lock.getUserId()); 213 result.setLockState( 214 org.opencms.workplace.list.Messages.get().getBundle(locale).key( 215 org.opencms.workplace.list.Messages.GUI_EXPLORER_LIST_ACTION_LOCK_NAME_2, 216 lockOwner.getName(), 217 lastProject)); 218 } else { 219 result.setLockState( 220 org.opencms.workplace.list.Messages.get().getBundle(locale).key( 221 org.opencms.workplace.list.Messages.GUI_EXPLORER_LIST_ACTION_UNLOCK_NAME_0)); 222 } 223 224 CmsProperty navText = CmsProperty.get(CmsPropertyDefinition.PROPERTY_NAVTEXT, properties); 225 if (navText != null) { 226 result.setNavText(navText.getValue()); 227 } 228 result.setPermissions(resourceUtil.getPermissionString()); 229 result.setSize(resource.getLength()); 230 result.setStateBean(resource.getState()); 231 CmsProperty title = CmsProperty.get(CmsPropertyDefinition.PROPERTY_TITLE, properties); 232 if (localizedTitle != null) { 233 result.setTitle(localizedTitle); 234 result.getListInfo().setTitle(localizedTitle); 235 } else if (title != null) { 236 result.setTitle(title.getValue()); 237 } 238 result.setUserCreated(resourceUtil.getUserCreated()); 239 result.setUserLastModified(resourceUtil.getUserLastModified()); 240 241 I_CmsResourceType resType = OpenCms.getResourceManager().getResourceType(resource); 242 result.setResourceType(resType.getTypeName()); 243 244 result.setStructureId(resource.getStructureId()); 245 if (resType instanceof CmsResourceTypeXmlContent) { 246 CmsFile file = cms.readFile(resource); 247 CmsXmlContent content = CmsXmlContentFactory.unmarshal(cms, file); 248 List<Locale> locales = content.getLocales(); 249 List<String> localeStrings = new ArrayList<String>(); 250 for (Locale l : locales) { 251 localeStrings.add(l.toString()); 252 } 253 result.setLocales(localeStrings); 254 } 255 Map<String, String> additionalAttributes = new LinkedHashMap<String, String>(); 256 additionalAttributes.put( 257 Messages.get().getBundle(locale).key(Messages.GUI_STATUS_PERMALINK_0), 258 OpenCms.getLinkManager().getPermalink(cms, cms.getSitePath(resource), detailContentId)); 259 260 if (OpenCms.getRoleManager().hasRole(cms, CmsRole.ADMINISTRATOR)) { 261 additionalAttributes.put( 262 Messages.get().getBundle(locale).key(Messages.GUI_STATUS_STRUCTURE_ID_0), 263 resource.getStructureId().toString()); 264 additionalAttributes.put( 265 Messages.get().getBundle(locale).key(Messages.GUI_STATUS_RESOURCE_ID_0), 266 resource.getResourceId().toString()); 267 } 268 result.setAdditionalAttributes(additionalAttributes); 269 270 List<CmsRelation> relations = cms.readRelations( 271 CmsRelationFilter.relationsToStructureId(resource.getStructureId())); 272 Map<CmsUUID, CmsResource> relationSources = new HashMap<CmsUUID, CmsResource>(); 273 274 if (CmsResourceTypeXmlContainerPage.isContainerPage(resource)) { 275 // People may link to the folder of a container page instead of the page itself 276 try { 277 CmsResource parent = cms.readParentFolder(resource.getStructureId()); 278 List<CmsRelation> parentRelations = cms.readRelations( 279 CmsRelationFilter.relationsToStructureId(parent.getStructureId())); 280 relations.addAll(parentRelations); 281 } catch (CmsException e) { 282 LOG.error(e.getLocalizedMessage(), e); 283 } 284 } 285 286 // find all distinct relation sources 287 for (CmsRelation relation : relations) { 288 try { 289 CmsResource currentSource = relation.getSource(cms, CmsResourceFilter.ALL); 290 relationSources.put(currentSource.getStructureId(), currentSource); 291 } catch (CmsException e) { 292 LOG.error(e.getLocalizedMessage(), e); 293 } 294 } 295 296 for (CmsResource relationResource : relationSources.values()) { 297 try { 298 CmsPermissionInfo permissionInfo = OpenCms.getADEManager().getPermissionInfo( 299 cms, 300 relationResource, 301 resource.getRootPath()); 302 if (permissionInfo.hasViewPermission()) { 303 CmsResourceStatusRelationBean relationBean = createRelationBean( 304 cms, 305 contentLocale, 306 relationResource, 307 permissionInfo); 308 CmsSite site = OpenCms.getSiteManager().getSiteForRootPath(relationResource.getRootPath()); 309 if ((site != null) 310 && !CmsStringUtil.isPrefixPath( 311 cms.getRequestContext().getSiteRoot(), 312 relationResource.getRootPath())) { 313 String siteTitle = site.getTitle(); 314 if (siteTitle == null) { 315 siteTitle = site.getUrl(); 316 } else { 317 siteTitle = CmsWorkplace.substituteSiteTitleStatic( 318 siteTitle, 319 OpenCms.getWorkplaceManager().getWorkplaceLocale(cms)); 320 } 321 relationBean.setSiteRoot(site.getSiteRoot()); 322 result.getOtherSiteRelationSources().add(relationBean); 323 relationBean.getInfoBean().setTitle( 324 "[" + siteTitle + "] " + relationBean.getInfoBean().getTitle()); 325 } else { 326 result.getRelationSources().add(relationBean); 327 } 328 329 } 330 } catch (CmsVfsResourceNotFoundException notfound) { 331 LOG.error(notfound.getLocalizedMessage(), notfound); 332 continue; 333 } 334 } 335 sortOtherSiteRelations(cms, result); 336 if (includeTargets) { 337 result.getRelationTargets().addAll(getTargets(cms, contentLocale, resource, additionalStructureIds)); 338 if ((detailContentId != null) && (realLocale != null)) { 339 // try to add detail only contents 340 try { 341 Optional<CmsResource> detailOnlyPage = CmsJspTagContainer.getDetailOnlyPage( 342 cms, 343 cms.readResource(detailContentId, CmsResourceFilter.ALL), 344 CmsJspTagContainer.getDetailContainerLocale(cms, realLocale.toString(), resource)); 345 if (detailOnlyPage.isPresent()) { 346 result.getRelationTargets().addAll( 347 getTargets( 348 cms, 349 contentLocale, 350 detailOnlyPage.get(), 351 Arrays.asList(detailOnlyPage.get().getStructureId()))); 352 } 353 354 } catch (CmsException e) { 355 LOG.error(e.getLocalizedMessage(), e); 356 } 357 } 358 Iterator<CmsResourceStatusRelationBean> iter = result.getRelationTargets().iterator(); 359 // Remove duplicates 360 Set<CmsUUID> visitedIds = Sets.newHashSet(); 361 while (iter.hasNext()) { 362 CmsResourceStatusRelationBean bean = iter.next(); 363 if (visitedIds.contains(bean.getStructureId())) { 364 iter.remove(); 365 } 366 visitedIds.add(bean.getStructureId()); 367 } 368 } 369 result.getSiblings().addAll(getSiblings(cms, contentLocale, resource)); 370 LinkedHashMap<CmsResourceStatusTabId, String> tabMap = new LinkedHashMap<CmsResourceStatusTabId, String>(); 371 Map<CmsResourceStatusTabId, CmsMessageContainer> tabs; 372 CmsResourceStatusTabId startTab = CmsResourceStatusTabId.tabRelationsFrom; 373 if (CmsResourceTypeXmlContainerPage.isContainerPage(resource)) { 374 tabs = CmsResourceStatusConstants.STATUS_TABS_CONTAINER_PAGE; 375 } else if (OpenCms.getResourceManager().getResourceType(resource) instanceof I_CmsLinkParseable) { 376 tabs = CmsResourceStatusConstants.STATUS_TABS_CONTENT; 377 } else { 378 tabs = CmsResourceStatusConstants.STATUS_TABS_OTHER; 379 startTab = CmsResourceStatusTabId.tabStatus; 380 } 381 for (Map.Entry<CmsResourceStatusTabId, CmsMessageContainer> entry : tabs.entrySet()) { 382 tabMap.put(entry.getKey(), entry.getValue().key(locale)); 383 } 384 385 result.setTabs(tabMap); 386 result.setStartTab(startTab); 387 return result; 388 } 389 390 /** 391 * Sorts relation beans from other sites by site order.<p> 392 * 393 * @param cms the current CMS context 394 * @param resStatus the bean in which to sort the relation beans 395 */ 396 public void sortOtherSiteRelations(CmsObject cms, CmsResourceStatusBean resStatus) { 397 398 final List<CmsSite> sites = OpenCms.getSiteManager().getAvailableSites( 399 cms, 400 false, 401 false, 402 cms.getRequestContext().getOuFqn()); 403 Collections.sort(resStatus.getOtherSiteRelationSources(), new Comparator<CmsResourceStatusRelationBean>() { 404 405 private Map<String, Integer> m_rankCache = Maps.newHashMap(); 406 407 public int compare(CmsResourceStatusRelationBean o1, CmsResourceStatusRelationBean o2) { 408 409 return ComparisonChain.start().compare(rank(o1), rank(o2)).compare( 410 o1.getSitePath(), 411 o2.getSitePath()).result(); 412 413 } 414 415 public int rank(CmsResourceStatusRelationBean r) { 416 417 if (m_rankCache.containsKey(r.getSiteRoot())) { 418 return m_rankCache.get(r.getSiteRoot()).intValue(); 419 } 420 421 int j = 0; 422 int result = Integer.MAX_VALUE; 423 for (CmsSite site : sites) { 424 if (site.getSiteRoot().equals(r.getSiteRoot())) { 425 result = j; 426 break; 427 } 428 j += 1; 429 } 430 431 m_rankCache.put(r.getSiteRoot(), new Integer(result)); 432 return result; 433 } 434 }); 435 } 436 437 /** 438 * Gets beans which represents the siblings of a resource.<p> 439 * 440 * @param cms the CMS ccontext 441 * @param locale the locale 442 * @param resource the resource 443 * @return the list of sibling beans 444 * 445 * @throws CmsException if something goes wrong 446 */ 447 protected List<CmsResourceStatusRelationBean> getSiblings(CmsObject cms, String locale, CmsResource resource) 448 throws CmsException { 449 450 List<CmsResourceStatusRelationBean> result = new ArrayList<CmsResourceStatusRelationBean>(); 451 for (CmsResource sibling : cms.readSiblings(resource, CmsResourceFilter.ALL)) { 452 if (sibling.getStructureId().equals(resource.getStructureId())) { 453 continue; 454 } 455 try { 456 CmsPermissionInfo permissionInfo = OpenCms.getADEManager().getPermissionInfo( 457 cms, 458 sibling, 459 resource.getRootPath()); 460 if (permissionInfo.hasViewPermission()) { 461 CmsResourceStatusRelationBean relationBean = createRelationBean( 462 cms, 463 locale, 464 sibling, 465 permissionInfo); 466 result.add(relationBean); 467 } 468 } catch (CmsException e) { 469 LOG.error(e.getLocalizedMessage(), e); 470 } 471 } 472 return result; 473 } 474 475 /** 476 * Gets the list of relation targets for a resource.<p> 477 * 478 * @param cms the current CMS context 479 * @param locale the locale 480 * @param resource the resource for which we want the relation targets 481 * @param additionalStructureIds structure ids of additional resources to include with the relation target 482 * 483 * @return the list of relation beans for the relation targets 484 * 485 * @throws CmsException if something goes wrong 486 */ 487 protected List<CmsResourceStatusRelationBean> getTargets( 488 CmsObject cms, 489 String locale, 490 CmsResource resource, 491 List<CmsUUID> additionalStructureIds) 492 throws CmsException { 493 494 CmsRelationTargetListBean listBean = getContainerpageRelationTargets( 495 cms, 496 resource.getStructureId(), 497 additionalStructureIds, 498 false); 499 List<CmsResourceStatusRelationBean> result = new ArrayList<CmsResourceStatusRelationBean>(); 500 for (CmsResource target : listBean.getResources()) { 501 try { 502 CmsPermissionInfo permissionInfo = OpenCms.getADEManager().getPermissionInfo( 503 cms, 504 target, 505 resource.getRootPath()); 506 if (permissionInfo.hasViewPermission()) { 507 CmsResourceStatusRelationBean relationBean = createRelationBean( 508 cms, 509 locale, 510 target, 511 permissionInfo); 512 result.add(relationBean); 513 } 514 } catch (CmsException e) { 515 LOG.error(e.getLocalizedMessage(), e); 516 } 517 } 518 return result; 519 520 } 521 522 /** 523 * Creates a bean for a single resource which is part of a relation list.<p> 524 * 525 * @param cms the current CMS context 526 * @param locale the locale 527 * @param relationResource the resource 528 * @param permissionInfo the permission info 529 * 530 * @return the status bean for the resource 531 * 532 * @throws CmsException if something goes wrong 533 */ 534 CmsResourceStatusRelationBean createRelationBean( 535 CmsObject cms, 536 String locale, 537 CmsResource relationResource, 538 CmsPermissionInfo permissionInfo) 539 throws CmsException { 540 541 CmsListInfoBean sourceBean = CmsVfsService.getPageInfo(cms, relationResource); 542 sourceBean.setMarkChangedState(true); 543 sourceBean.setResourceState(relationResource.getState()); 544 545 if (!CmsStringUtil.isEmptyOrWhitespaceOnly(locale)) { 546 Locale realLocale = CmsLocaleManager.getLocale(locale); 547 CmsGallerySearchResult result = CmsGallerySearch.searchById( 548 cms, 549 relationResource.getStructureId(), 550 realLocale); 551 if (!CmsStringUtil.isEmptyOrWhitespaceOnly(result.getTitle())) { 552 sourceBean.setTitle(result.getTitle()); 553 } 554 } 555 String link = null; 556 try { 557 link = OpenCms.getLinkManager().substituteLink(cms, relationResource); 558 } catch (Exception e) { 559 LOG.warn(e.getLocalizedMessage(), e); 560 } 561 CmsResourceStatusRelationBean relationBean = new CmsResourceStatusRelationBean( 562 sourceBean, 563 link, 564 relationResource.getStructureId(), 565 permissionInfo); 566 if (CmsResourceTypeXmlContent.isXmlContent(relationResource)) { 567 relationBean.setIsXmlContent(true); 568 } 569 String sitePath = cms.getSitePath(relationResource); 570 relationBean.setSitePath(sitePath); 571 return relationBean; 572 } 573 574}