001/* 002 * (C) Copyright 2006-2007 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> 016 * 017 * $Id: RestDocumentLink.java 25089 2007-09-18 17:41:58Z ogrisel $ 018 */ 019 020package org.nuxeo.ecm.platform.ui.web.component.document; 021 022import java.io.IOException; 023import java.util.ArrayList; 024import java.util.LinkedHashMap; 025import java.util.Map; 026 027import javax.el.ELException; 028import javax.el.ValueExpression; 029import javax.faces.FacesException; 030import javax.faces.component.ContextCallback; 031import javax.faces.component.UIComponent; 032import javax.faces.component.UIParameter; 033import javax.faces.component.html.HtmlOutputLink; 034import javax.faces.context.FacesContext; 035import javax.faces.event.FacesEvent; 036 037import org.apache.commons.lang.StringUtils; 038import org.nuxeo.ecm.core.api.DocumentLocation; 039import org.nuxeo.ecm.core.api.DocumentModel; 040import org.nuxeo.ecm.core.api.DocumentRef; 041import org.nuxeo.ecm.core.api.IdRef; 042import org.nuxeo.ecm.core.api.impl.DocumentLocationImpl; 043import org.nuxeo.ecm.platform.ui.web.api.WebActions; 044import org.nuxeo.ecm.platform.ui.web.component.VariableManager; 045import org.nuxeo.ecm.platform.ui.web.tag.fn.DocumentModelFunctions; 046import org.nuxeo.ecm.platform.ui.web.util.BaseURL; 047 048import com.sun.faces.renderkit.html_basic.HtmlBasicRenderer.Param; 049 050/** 051 * Component that gives generates a Restful link given a document. 052 * 053 * @author <a href="mailto:at@nuxeo.com">Anahide Tchertchian</a> 054 */ 055public class RestDocumentLink extends HtmlOutputLink { 056 057 public static final String COMPONENT_TYPE = RestDocumentLink.class.getName(); 058 059 public static final String COMPONENT_FAMILY = RestDocumentLink.class.getName(); 060 061 public static final String DEFAULT_VIEW_ID = "view_documents"; 062 063 protected static final Param[] EMPTY_PARAMS = new Param[0]; 064 065 protected DocumentModel document; 066 067 /** 068 * @since 5.7 069 */ 070 protected String repositoryName; 071 072 protected DocumentRef documentIdRef; 073 074 /** 075 * @since 7.4 076 */ 077 protected DocumentRef documentPathRef; 078 079 protected String view; 080 081 protected String tab; 082 083 protected String subTab; 084 085 /** 086 * @since 5.4.2 087 */ 088 protected String tabs; 089 090 protected Boolean addTabInfo; 091 092 protected String pattern; 093 094 protected Boolean newConversation; 095 096 /** 097 * @since 7.3 098 */ 099 protected String baseURL; 100 101 /** 102 * @since 5.7 103 */ 104 protected String var; 105 106 /** 107 * @since 5.7 108 */ 109 protected Boolean resolveOnly; 110 111 @Override 112 public String getFamily() { 113 return COMPONENT_FAMILY; 114 } 115 116 /** 117 * Override to build the URL thanks to other tag attributes information. 118 * <p> 119 * The document view service is queried to build it, and the tag attribute named "value" is ignored. 120 */ 121 @Override 122 public Object getValue() { 123 DocumentModel doc = getDocument(); 124 DocumentRef documentIdRef = getDocumentIdRef(); 125 DocumentRef documentPathRef = getDocumentPathRef(); 126 String repoName = getRepositoryName(); 127 if (doc == null && repoName == null || (documentIdRef != null || documentPathRef != null) && repoName == null) { 128 return null; 129 } 130 131 String viewId = getView(); 132 133 Map<String, String> params = new LinkedHashMap<String, String>(); 134 String tabValue = getTab(); 135 String subTabValue = getSubTab(); 136 String tabValues = getTabs(); 137 if (tabValues == null) { 138 tabValues = ""; 139 } 140 if (tabValue != null && !tabValue.isEmpty()) { 141 if (!tabValues.isEmpty()) { 142 tabValues += ","; 143 } 144 tabValues += ":" + tabValue; 145 // params.put("tabId", tabValue); // BBB 146 if (subTabValue != null) { 147 tabValues += ":" + subTabValue; 148 // params.put("subTabId", subTabValue); // BBB 149 } 150 } else { 151 if (Boolean.TRUE.equals(getAddTabInfo())) { 152 // reset tab info, resetting the tab will reset the sub tab 153 if (!tabValues.isEmpty()) { 154 tabValues += ","; 155 } 156 tabValues += ":" + WebActions.NULL_TAB_ID; 157 // params.put("tabId", WebActions.NULL_TAB_ID); // BBB 158 } 159 } 160 if (!tabValues.isEmpty()) { 161 params.put("tabIds", tabValues); 162 } 163 164 // add parameters from f:param sub tags 165 Param[] paramTags = getParamList(); 166 for (Param param : paramTags) { 167 String pn = param.name; 168 if (pn != null && pn.length() != 0) { 169 String pv = param.value; 170 if (pv != null && pv.length() != 0) { 171 params.put(pn, pv); 172 } 173 } 174 } 175 176 String pattern = getPattern(); 177 String baseURL = getBaseURL(); 178 if (StringUtils.isEmpty(baseURL)) { 179 baseURL = BaseURL.getBaseURL(); 180 } 181 182 // new conversation variable handled by renderer 183 boolean useNewConversation = true; 184 if (doc != null) { 185 return DocumentModelFunctions.documentUrl(pattern, doc, viewId, params, useNewConversation, baseURL); 186 } else if (documentIdRef != null) { 187 DocumentLocation docLoc = new DocumentLocationImpl(repoName, documentIdRef); 188 return DocumentModelFunctions.documentUrl(pattern, docLoc, viewId, params, useNewConversation, baseURL); 189 } else if (documentPathRef != null) { 190 DocumentLocation docLoc = new DocumentLocationImpl(repoName, documentPathRef); 191 return DocumentModelFunctions.documentUrl(pattern, docLoc, viewId, params, useNewConversation, baseURL); 192 } else { 193 return DocumentModelFunctions.repositoryUrl(pattern, repoName, viewId, params, useNewConversation, baseURL); 194 } 195 } 196 197 protected Param[] getParamList() { 198 if (getChildCount() > 0) { 199 ArrayList<Param> parameterList = new ArrayList<Param>(); 200 for (UIComponent kid : getChildren()) { 201 if (kid instanceof UIParameter) { 202 UIParameter uiParam = (UIParameter) kid; 203 Object value = uiParam.getValue(); 204 Param param = new Param(uiParam.getName(), (value == null ? null : value.toString())); 205 parameterList.add(param); 206 } 207 } 208 return parameterList.toArray(new Param[parameterList.size()]); 209 } else { 210 return EMPTY_PARAMS; 211 } 212 213 } 214 215 // setters and getters for tag attributes 216 217 public String getPattern() { 218 if (pattern != null) { 219 return pattern; 220 } 221 ValueExpression ve = getValueExpression("pattern"); 222 if (ve != null) { 223 try { 224 return (String) ve.getValue(getFacesContext().getELContext()); 225 } catch (ELException e) { 226 throw new FacesException(e); 227 } 228 } else { 229 return null; 230 } 231 } 232 233 public void setPattern(String codec) { 234 pattern = codec; 235 } 236 237 public DocumentModel getDocument() { 238 if (document != null) { 239 return document; 240 } 241 ValueExpression ve = getValueExpression("document"); 242 if (ve != null) { 243 try { 244 return (DocumentModel) ve.getValue(getFacesContext().getELContext()); 245 } catch (ELException e) { 246 throw new FacesException(e); 247 } 248 } else { 249 return null; 250 } 251 } 252 253 public void setDocument(DocumentModel document) { 254 this.document = document; 255 } 256 257 public String getRepositoryName() { 258 if (repositoryName != null) { 259 return repositoryName; 260 } 261 ValueExpression ve = getValueExpression("repositoryName"); 262 if (ve != null) { 263 try { 264 return (String) ve.getValue(getFacesContext().getELContext()); 265 } catch (ELException e) { 266 throw new FacesException(e); 267 } 268 } else { 269 return null; 270 } 271 } 272 273 public void setRepositoryName(String repositoryName) { 274 this.repositoryName = repositoryName; 275 } 276 277 public DocumentRef getDocumentIdRef() { 278 if (documentIdRef != null) { 279 return documentIdRef; 280 } 281 ValueExpression ve = getValueExpression("documentId"); 282 if (ve != null) { 283 try { 284 String id = (String) ve.getValue(getFacesContext().getELContext()); 285 if (id != null) { 286 return new IdRef(id); 287 } else { 288 return null; 289 } 290 } catch (ELException e) { 291 throw new FacesException(e); 292 } 293 } else { 294 return null; 295 } 296 } 297 298 public void setDocumentIdRef(DocumentRef documentIdRef) { 299 this.documentIdRef = documentIdRef; 300 } 301 302 public DocumentRef getDocumentPathRef() { 303 if (documentPathRef != null) { 304 return documentPathRef; 305 } 306 ValueExpression ve = getValueExpression("documentPath"); 307 if (ve != null) { 308 try { 309 String id = (String) ve.getValue(getFacesContext().getELContext()); 310 if (id != null) { 311 return new IdRef(id); 312 } else { 313 return null; 314 } 315 } catch (ELException e) { 316 throw new FacesException(e); 317 } 318 } else { 319 return null; 320 } 321 } 322 323 public void setDocumentPathRef(DocumentRef documentPathRef) { 324 this.documentPathRef = documentPathRef; 325 } 326 327 /** 328 * Returns true if URL must link to a page in a new conversation. 329 * <p> 330 * Defaults to false. 331 */ 332 public Boolean getNewConversation() { 333 if (newConversation != null) { 334 return newConversation; 335 } 336 ValueExpression ve = getValueExpression("newConversation"); 337 if (ve != null) { 338 try { 339 return Boolean.valueOf(!Boolean.FALSE.equals(ve.getValue(getFacesContext().getELContext()))); 340 } catch (ELException e) { 341 throw new FacesException(e); 342 } 343 } else { 344 // default value 345 return Boolean.FALSE; 346 } 347 } 348 349 public void setNewConversation(Boolean newConversation) { 350 this.newConversation = newConversation; 351 } 352 353 public String getSubTab() { 354 if (subTab != null) { 355 return subTab; 356 } 357 ValueExpression ve = getValueExpression("subTab"); 358 if (ve != null) { 359 try { 360 return (String) ve.getValue(getFacesContext().getELContext()); 361 } catch (ELException e) { 362 throw new FacesException(e); 363 } 364 } else { 365 return null; 366 } 367 } 368 369 public void setSubTab(String subTab) { 370 this.subTab = subTab; 371 } 372 373 public String getTab() { 374 if (tab != null) { 375 return tab; 376 } 377 ValueExpression ve = getValueExpression("tab"); 378 if (ve != null) { 379 try { 380 return (String) ve.getValue(getFacesContext().getELContext()); 381 } catch (ELException e) { 382 throw new FacesException(e); 383 } 384 } else { 385 return null; 386 } 387 } 388 389 public void setTab(String tab) { 390 this.tab = tab; 391 } 392 393 /** 394 * @since 7.3 395 */ 396 public String getBaseURL() { 397 if (baseURL != null) { 398 return baseURL; 399 } 400 ValueExpression ve = getValueExpression("baseURL"); 401 if (ve != null) { 402 try { 403 return (String) ve.getValue(getFacesContext().getELContext()); 404 } catch (ELException e) { 405 throw new FacesException(e); 406 } 407 } else { 408 return null; 409 } 410 } 411 412 /** 413 * @since 7.3 414 */ 415 public void setBaseURL(String baseURL) { 416 this.baseURL = baseURL; 417 } 418 419 public String getView() { 420 if (view != null) { 421 return view; 422 } 423 ValueExpression ve = getValueExpression("view"); 424 if (ve != null) { 425 try { 426 return (String) ve.getValue(getFacesContext().getELContext()); 427 } catch (ELException e) { 428 throw new FacesException(e); 429 } 430 } else { 431 return null; 432 } 433 } 434 435 public void setView(String view) { 436 this.view = view; 437 } 438 439 public Boolean getAddTabInfo() { 440 if (addTabInfo != null) { 441 return addTabInfo; 442 } 443 ValueExpression ve = getValueExpression("addTabInfo"); 444 if (ve != null) { 445 try { 446 return Boolean.valueOf(!Boolean.FALSE.equals(ve.getValue(getFacesContext().getELContext()))); 447 } catch (ELException e) { 448 throw new FacesException(e); 449 } 450 } else { 451 // default value 452 return Boolean.TRUE; 453 } 454 } 455 456 public void setAddTabInfo(Boolean addTabInfo) { 457 this.addTabInfo = addTabInfo; 458 } 459 460 public String getTabs() { 461 if (tabs != null) { 462 return tabs; 463 } 464 ValueExpression ve = getValueExpression("tabs"); 465 if (ve != null) { 466 try { 467 return (String) ve.getValue(getFacesContext().getELContext()); 468 } catch (ELException e) { 469 throw new FacesException(e); 470 } 471 } else { 472 return null; 473 } 474 } 475 476 public void setTabs(String tabs) { 477 this.tabs = tabs; 478 } 479 480 public Boolean getResolveOnly() { 481 if (resolveOnly != null) { 482 return resolveOnly; 483 } 484 ValueExpression ve = getValueExpression("resolveOnly"); 485 if (ve != null) { 486 try { 487 return Boolean.valueOf(!Boolean.FALSE.equals(ve.getValue(getFacesContext().getELContext()))); 488 } catch (ELException e) { 489 throw new FacesException(e); 490 } 491 } else { 492 // default value 493 return Boolean.FALSE; 494 } 495 } 496 497 public void setResolveOnly(Boolean resolveOnly) { 498 this.resolveOnly = resolveOnly; 499 } 500 501 public String getVar() { 502 if (var != null) { 503 return var; 504 } 505 ValueExpression ve = getValueExpression("var"); 506 if (ve != null) { 507 try { 508 return (String) ve.getValue(getFacesContext().getELContext()); 509 } catch (ELException e) { 510 throw new FacesException(e); 511 } 512 } else { 513 return null; 514 } 515 } 516 517 public void setVar(String var) { 518 this.var = var; 519 } 520 521 // "resolveOnly" attribute management: expose value instead of rendering 522 // the tag 523 524 /** 525 * Saves the current value exposed as param to the request, and put new variable value instead. 526 * <p> 527 * Returns the original value exposed to the request. 528 * 529 * @since 5.7 530 */ 531 protected Object beforeRender() { 532 String var = getVar(); 533 Object orig = VariableManager.saveRequestMapVarValue(var); 534 if (Boolean.TRUE.equals(getResolveOnly())) { 535 VariableManager.putVariableToRequestParam(var, getValue()); 536 } 537 return orig; 538 } 539 540 /** 541 * Restored the original value exposed as param to the request, and remove current variable value. 542 * 543 * @since 5.7 544 */ 545 protected void afterRender(Object origVarValue) { 546 String var = getVar(); 547 VariableManager.restoreRequestMapVarValue(var, origVarValue); 548 } 549 550 /** 551 * @since 5.7 552 */ 553 @Override 554 public boolean invokeOnComponent(FacesContext context, String clientId, ContextCallback callback) 555 throws FacesException { 556 Object varValue = beforeRender(); 557 try { 558 return super.invokeOnComponent(context, clientId, callback); 559 } finally { 560 afterRender(varValue); 561 } 562 } 563 564 /** 565 * @since 5.7 566 */ 567 @Override 568 public void broadcast(FacesEvent event) { 569 Object varValue = beforeRender(); 570 try { 571 super.broadcast(event); 572 } finally { 573 afterRender(varValue); 574 } 575 } 576 577 /** 578 * @since 5.7 579 */ 580 @Override 581 public void encodeBegin(FacesContext context) throws IOException { 582 if (!Boolean.TRUE.equals(getResolveOnly())) { 583 super.encodeBegin(context); 584 } 585 } 586 587 @Override 588 public void encodeChildren(FacesContext context) throws IOException { 589 Object varValue = beforeRender(); 590 try { 591 super.encodeChildren(context); 592 } finally { 593 afterRender(varValue); 594 } 595 } 596 597 /** 598 * @since 5.7 599 */ 600 @Override 601 public void encodeEnd(FacesContext context) throws IOException { 602 if (!Boolean.TRUE.equals(getResolveOnly())) { 603 super.encodeEnd(context); 604 } 605 } 606 607 // state holder 608 609 @Override 610 public Object saveState(FacesContext context) { 611 return new Object[] { super.saveState(context), document, documentIdRef, view, tab, subTab, tabs, addTabInfo, 612 pattern, newConversation, baseURL, var, resolveOnly }; 613 } 614 615 @Override 616 public void restoreState(FacesContext context, Object state) { 617 Object[] values = (Object[]) state; 618 super.restoreState(context, values[0]); 619 document = (DocumentModel) values[1]; 620 documentIdRef = (DocumentRef) values[2]; 621 view = (String) values[3]; 622 tab = (String) values[4]; 623 subTab = (String) values[5]; 624 tabs = (String) values[6]; 625 addTabInfo = (Boolean) values[7]; 626 pattern = (String) values[8]; 627 newConversation = (Boolean) values[9]; 628 baseURL = (String) values[10]; 629 var = (String) values[11]; 630 resolveOnly = (Boolean) values[12]; 631 } 632 633}