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