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