0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. The ASF licenses this file to You
0004: * under the Apache License, Version 2.0 (the "License"); you may not
0005: * use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License. For additional information regarding
0015: * copyright in this work, please see the NOTICE file in the top level
0016: * directory of this distribution.
0017: */
0018:
0019: package org.apache.roller.pojos;
0020:
0021: import java.io.Serializable;
0022: import java.io.UnsupportedEncodingException;
0023: import java.net.URLEncoder;
0024: import java.sql.Timestamp;
0025: import java.text.SimpleDateFormat;
0026: import java.util.ArrayList;
0027: import java.util.Arrays;
0028: import java.util.Calendar;
0029: import java.util.Date;
0030: import java.util.HashMap;
0031: import java.util.HashSet;
0032: import java.util.Iterator;
0033: import java.util.List;
0034: import java.util.Locale;
0035: import java.util.Map;
0036: import java.util.Set;
0037: import java.util.StringTokenizer;
0038: import java.util.TreeSet;
0039:
0040: import org.apache.commons.lang.StringEscapeUtils;
0041:
0042: import org.apache.commons.lang.StringUtils;
0043: import org.apache.commons.logging.Log;
0044: import org.apache.commons.logging.LogFactory;
0045: import org.apache.roller.RollerException;
0046: import org.apache.roller.config.RollerRuntimeConfig;
0047: import org.apache.roller.business.Roller;
0048: import org.apache.roller.business.RollerFactory;
0049: import org.apache.roller.business.UserManager;
0050: import org.apache.roller.business.WeblogEntryPlugin;
0051: import org.apache.roller.business.WeblogManager;
0052: import org.apache.roller.util.DateUtil;
0053: import org.apache.roller.util.MessageUtilities;
0054: import org.apache.roller.util.URLUtilities;
0055: import org.apache.roller.util.Utilities;
0056:
0057: /**
0058: * Represents a Weblog Entry.
0059: *
0060: * @ejb:bean name="WeblogEntryData"
0061: * @struts.form include-all="true"
0062: * @hibernate.class lazy="false" table="weblogentry"
0063: * @hibernate.cache usage="read-write"
0064: */
0065: public class WeblogEntryData extends PersistentObject implements
0066: Serializable {
0067: private static Log mLogger = LogFactory.getFactory().getInstance(
0068: WeblogEntryData.class);
0069:
0070: public static final long serialVersionUID = 2341505386843044125L;
0071:
0072: public static final String DRAFT = "DRAFT";
0073: public static final String PUBLISHED = "PUBLISHED";
0074: public static final String PENDING = "PENDING";
0075:
0076: // Simple properies
0077: private String id = null;
0078: private String title = null;
0079: private String link = null;
0080: private String summary = null;
0081: private String text = null;
0082: private String contentType = null;
0083: private String contentSrc = null;
0084: private String anchor = null;
0085: private Timestamp pubTime = null;
0086: private Timestamp updateTime = null;
0087: private String plugins = null;
0088: private Boolean allowComments = Boolean.TRUE;
0089: private Integer commentDays = new Integer(7);
0090: private Boolean rightToLeft = Boolean.FALSE;
0091: private Boolean pinnedToMain = Boolean.FALSE;
0092: private String status = DRAFT;
0093: private String locale = null;
0094:
0095: // Associated objects
0096: private UserData creator = null;
0097: private WebsiteData website = null;
0098: private WeblogCategoryData category = null;
0099:
0100: // Collection of name/value entry attributes
0101: private Map attMap = new HashMap();
0102: private Set attSet = new TreeSet();
0103:
0104: private Set tagSet = new HashSet();
0105: private Set removedTags = new HashSet();
0106: private Set addedTags = new HashSet();
0107:
0108: //----------------------------------------------------------- Construction
0109:
0110: public WeblogEntryData() {
0111: }
0112:
0113: public WeblogEntryData(String id, WeblogCategoryData category,
0114: WebsiteData website, UserData creator, String title,
0115: String link, String text, String anchor, Timestamp pubTime,
0116: Timestamp updateTime, String status) {
0117: this .id = id;
0118: this .category = category;
0119: this .website = website;
0120: this .creator = creator;
0121: this .title = title;
0122: this .link = link;
0123: this .text = text;
0124: this .anchor = anchor;
0125: this .pubTime = pubTime;
0126: this .updateTime = updateTime;
0127: this .status = status;
0128: }
0129:
0130: public WeblogEntryData(WeblogEntryData otherData) {
0131: this .setData(otherData);
0132: }
0133:
0134: //---------------------------------------------------------- Initializaion
0135:
0136: /**
0137: * Setter is needed in RollerImpl.storePersistentObject()
0138: */
0139: public void setData(PersistentObject otherData) {
0140: WeblogEntryData other = (WeblogEntryData) otherData;
0141:
0142: this .id = other.getId();
0143: this .category = other.getCategory();
0144: this .website = other.getWebsite();
0145: this .creator = other.getCreator();
0146: this .title = other.getTitle();
0147: this .link = other.getLink();
0148: this .text = other.getText();
0149: this .anchor = other.getAnchor();
0150: this .pubTime = other.getPubTime();
0151: this .updateTime = other.getUpdateTime();
0152: this .status = other.getStatus();
0153: this .plugins = other.getPlugins();
0154: this .allowComments = other.getAllowComments();
0155: this .commentDays = other.getCommentDays();
0156: this .rightToLeft = other.getRightToLeft();
0157: this .pinnedToMain = other.getPinnedToMain();
0158: }
0159:
0160: //------------------------------------------------------ Simple properties
0161:
0162: /**
0163: * @roller.wrapPojoMethod type="simple"
0164: * @ejb:persistent-field
0165: * @hibernate.id column="id" generator-class="uuid.hex" unsaved-value="null"
0166: */
0167: public String getId() {
0168: return this .id;
0169: }
0170:
0171: /** @ejb:persistent-field */
0172: public void setId(String id) {
0173: this .id = id;
0174: }
0175:
0176: /**
0177: * @roller.wrapPojoMethod type="pojo"
0178: * @ejb:persistent-field
0179: * @hibernate.many-to-one column="categoryid" cascade="none" not-null="true"
0180: */
0181: public WeblogCategoryData getCategory() {
0182: return this .category;
0183: }
0184:
0185: /** @ejb:persistent-field */
0186: public void setCategory(WeblogCategoryData category) {
0187: this .category = category;
0188: }
0189:
0190: /**
0191: * Set weblog category via weblog category ID.
0192: * @param id Weblog category ID.
0193: */
0194: public void setCategoryId(String id) throws RollerException {
0195: WeblogManager wmgr = RollerFactory.getRoller()
0196: .getWeblogManager();
0197: setCategory(wmgr.getWeblogCategory(id));
0198: }
0199:
0200: /**
0201: * Return collection of WeblogCategoryData objects of this entry.
0202: * Added for symetry with PlanetEntryData object.
0203: * @roller.wrapPojoMethod type="pojo-collection" class="org.apache.roller.pojos.WeblogCategoryData"
0204: */
0205: public List getCategories() {
0206: List cats = new ArrayList();
0207: cats.add(getCategory());
0208: return cats;
0209: }
0210:
0211: /** No-op method to please XDoclet */
0212: public void setCategories(List cats) {
0213: // no-op
0214: }
0215:
0216: /**
0217: * @roller.wrapPojoMethod type="pojo"
0218: * @ejb:persistent-field
0219: * @hibernate.many-to-one column="websiteid" cascade="none" not-null="true"
0220: */
0221: public WebsiteData getWebsite() {
0222: return this .website;
0223: }
0224:
0225: /** @ejb:persistent-field */
0226: public void setWebsite(WebsiteData website) {
0227: this .website = website;
0228: }
0229:
0230: /**
0231: * @roller.wrapPojoMethod type="simple"
0232: * @ejb:persistent-field
0233: * @hibernate.many-to-one column="userid" cascade="none" not-null="true"
0234: */
0235: public UserData getCreator() {
0236: return this .creator;
0237: }
0238:
0239: /** @ejb:persistent-field */
0240: public void setCreator(UserData creator) {
0241: this .creator = creator;
0242: }
0243:
0244: /**
0245: * @roller.wrapPojoMethod type="simple"
0246: * @ejb:persistent-field
0247: * @hibernate.property column="title" non-null="true" unique="false"
0248: */
0249: public String getTitle() {
0250: return this .title;
0251: }
0252:
0253: /** @ejb:persistent-field */
0254: public void setTitle(String title) {
0255: this .title = title;
0256: }
0257:
0258: /**
0259: * Get summary for weblog entry (maps to RSS description and Atom summary).
0260: * @roller.wrapPojoMethod type="simple"
0261: * @ejb:persistent-field
0262: * @hibernate.property column="summary" non-null="false" unique="false"
0263: */
0264: public String getSummary() {
0265: return summary;
0266: }
0267:
0268: /**
0269: * Set summary for weblog entry (maps to RSS description and Atom summary).
0270: * @ejb:persistent-field
0271: */
0272: public void setSummary(String summary) {
0273: this .summary = summary;
0274: }
0275:
0276: /**
0277: * Get content text for weblog entry (maps to RSS content:encoded and Atom content).
0278: * @roller.wrapPojoMethod type="simple"
0279: * @ejb:persistent-field
0280: * @hibernate.property column="text" non-null="true" unique="false"
0281: */
0282: public String getText() {
0283: return this .text;
0284: }
0285:
0286: /**
0287: * Set content text for weblog entry (maps to RSS content:encoded and Atom content).
0288: * @ejb:persistent-field
0289: */
0290: public void setText(String text) {
0291: this .text = text;
0292: }
0293:
0294: /**
0295: * Get content type (text, html, xhtml or a MIME content type)
0296: * @roller.wrapPojoMethod type="simple"
0297: * @ejb:persistent-field
0298: * @hibernate.property column="content_type" non-null="false" unique="false"
0299: */
0300: public String getContentType() {
0301: return contentType;
0302: }
0303:
0304: /**
0305: * Set content type (text, html, xhtml or a MIME content type)
0306: * @ejb:persistent-field
0307: */
0308: public void setContentType(String contentType) {
0309: this .contentType = contentType;
0310: }
0311:
0312: /**
0313: * Get URL for out-of-line content.
0314: * @roller.wrapPojoMethod type="simple"
0315: * @ejb:persistent-field
0316: * @hibernate.property column="content_src" non-null="false" unique="false"
0317: */
0318: public String getContentSrc() {
0319: return contentSrc;
0320: }
0321:
0322: /**
0323: * Set URL for out-of-line content.
0324: * @ejb:persistent-field
0325: */
0326: public void setContentSrc(String contentSrc) {
0327: this .contentSrc = contentSrc;
0328: }
0329:
0330: /**
0331: * @roller.wrapPojoMethod type="simple"
0332: * @ejb:persistent-field
0333: * @hibernate.property column="anchor" non-null="true" unique="false"
0334: */
0335: public String getAnchor() {
0336: return this .anchor;
0337: }
0338:
0339: /** @ejb:persistent-field */
0340: public void setAnchor(String anchor) {
0341: this .anchor = anchor;
0342: }
0343:
0344: //-------------------------------------------------------------------------
0345: /**
0346: * Map attributes as set because XDoclet 1.2b4 map support is broken.
0347: *
0348: * @roller.wrapPojoMethod type="pojo-collection" class="org.apache.roller.pojos.EntryAttributeData"
0349: * @ejb:persistent-field
0350: * @hibernate.set lazy="true" order-by="name" inverse="true" cascade="all-delete-orphan"
0351: * @hibernate.collection-key column="entryid" type="String"
0352: * @hibernate.collection-one-to-many class="org.apache.roller.pojos.EntryAttributeData"
0353: */
0354: public Set getEntryAttributes() {
0355: return attSet;
0356: }
0357:
0358: /** @ejb:persistent-field */
0359: public void setEntryAttributes(Set attSet) {
0360: this .attSet = attSet;
0361:
0362: // copy set to map
0363: if (attSet != null) {
0364: this .attSet = attSet;
0365: this .attMap = new HashMap();
0366: Iterator iter = this .attSet.iterator();
0367: while (iter.hasNext()) {
0368: EntryAttributeData att = (EntryAttributeData) iter
0369: .next();
0370: attMap.put(att.getName(), att);
0371: }
0372: } else {
0373: this .attSet = new TreeSet();
0374: this .attMap = new HashMap();
0375: }
0376: }
0377:
0378: /**
0379: * Would be named getEntryAttribute, but that would set off XDoclet
0380: *
0381: * @roller.wrapPojoMethod type="simple"
0382: */
0383: public String findEntryAttribute(String name) {
0384: EntryAttributeData att = ((EntryAttributeData) attMap.get(name));
0385: return (att != null) ? att.getValue() : null;
0386: }
0387:
0388: public void putEntryAttribute(String name, String value)
0389: throws Exception {
0390: EntryAttributeData att = (EntryAttributeData) attMap.get(name);
0391: if (att == null) {
0392: att = new EntryAttributeData();
0393: att.setEntry(this );
0394: att.setName(name);
0395: att.setValue(value);
0396: attMap.put(name, att);
0397: attSet.add(att);
0398: } else {
0399: att.setValue(value);
0400: }
0401: }
0402:
0403: public void removeEntryAttribute(String name)
0404: throws RollerException {
0405: EntryAttributeData att = (EntryAttributeData) attMap.get(name);
0406: if (att != null) {
0407: attMap.remove(att);
0408: attSet.remove(att);
0409: }
0410: }
0411:
0412: //-------------------------------------------------------------------------
0413:
0414: /**
0415: * <p>Publish time is the time that an entry is to be (or was) made available
0416: * for viewing by newsfeed readers and visitors to the Roller site.</p>
0417: *
0418: * <p>Roller stores time using the timeZone of the server itself. When
0419: * times are displayed in a user's weblog they must be translated
0420: * to the user's timeZone.</p>
0421: *
0422: * <p>NOTE: Times are stored using the SQL TIMESTAMP datatype, which on
0423: * MySQL has only a one-second resolution.</p>
0424: *
0425: * @roller.wrapPojoMethod type="simple"
0426: * @ejb:persistent-field
0427: * @hibernate.property column="pubtime" non-null="true" unique="false"
0428: */
0429: public Timestamp getPubTime() {
0430: return this .pubTime;
0431: }
0432:
0433: /** @ejb:persistent-field */
0434: public void setPubTime(Timestamp pubTime) {
0435: this .pubTime = pubTime;
0436: }
0437:
0438: /**
0439: * <p>Update time is the last time that an weblog entry was saved in the
0440: * Roller weblog editor or via web services API (XML-RPC or Atom).</p>
0441: *
0442: * <p>Roller stores time using the timeZone of the server itself. When
0443: * times are displayed in a user's weblog they must be translated
0444: * to the user's timeZone.</p>
0445: *
0446: * <p>NOTE: Times are stored using the SQL TIMESTAMP datatype, which on
0447: * MySQL has only a one-second resolution.</p>
0448: *
0449: * @roller.wrapPojoMethod type="simple"
0450: * @ejb:persistent-field
0451: * @hibernate.property column="updatetime" non-null="true" unique="false"
0452: */
0453: public Timestamp getUpdateTime() {
0454: return this .updateTime;
0455: }
0456:
0457: /** @ejb:persistent-field */
0458: public void setUpdateTime(Timestamp updateTime) {
0459: this .updateTime = updateTime;
0460: }
0461:
0462: /**
0463: * @roller.wrapPojoMethod type="simple"
0464: * @ejb:persistent-field
0465: * @hibernate.property column="status" non-null="true" unique="false"
0466: */
0467: public String getStatus() {
0468: return this .status;
0469: }
0470:
0471: /** @ejb:persistent-field */
0472: public void setStatus(String status) {
0473: this .status = status;
0474: }
0475:
0476: /**
0477: * Some weblog entries are about one specific link.
0478: * @return Returns the link.
0479: *
0480: * @roller.wrapPojoMethod type="simple"
0481: * @ejb:persistent-field
0482: * @hibernate.property column="link" non-null="false" unique="false"
0483: */
0484: public String getLink() {
0485: return link;
0486: }
0487:
0488: /**
0489: * @ejb:persistent-field
0490: * @param link The link to set.
0491: */
0492: public void setLink(String link) {
0493: this .link = link;
0494: }
0495:
0496: /**
0497: * Comma-delimited list of this entry's Plugins.
0498: *
0499: * @roller.wrapPojoMethod type="simple"
0500: * @ejb:persistent-field
0501: * @hibernate.property column="plugins" non-null="false" unique="false"
0502: */
0503: public String getPlugins() {
0504: return plugins;
0505: }
0506:
0507: /** @ejb:persistent-field */
0508: public void setPlugins(String string) {
0509: plugins = string;
0510: }
0511:
0512: /**
0513: * True if comments are allowed on this weblog entry.
0514: *
0515: * @roller.wrapPojoMethod type="simple"
0516: * @ejb:persistent-field
0517: * @hibernate.property column="allowcomments" non-null="true" unique="false"
0518: */
0519: public Boolean getAllowComments() {
0520: return allowComments;
0521: }
0522:
0523: /**
0524: * True if comments are allowed on this weblog entry.
0525: * @ejb:persistent-field
0526: */
0527: public void setAllowComments(Boolean allowComments) {
0528: this .allowComments = allowComments;
0529: }
0530:
0531: /**
0532: * Number of days after pubTime that comments should be allowed, or 0 for no limit.
0533: *
0534: * @roller.wrapPojoMethod type="simple"
0535: * @ejb:persistent-field
0536: * @hibernate.property column="commentdays" non-null="true" unique="false"
0537: */
0538: public Integer getCommentDays() {
0539: return commentDays;
0540: }
0541:
0542: /**
0543: * Number of days after pubTime that comments should be allowed, or 0 for no limit.
0544: * @ejb:persistent-field
0545: */
0546: public void setCommentDays(Integer commentDays) {
0547: this .commentDays = commentDays;
0548: }
0549:
0550: /**
0551: * True if this entry should be rendered right to left.
0552: *
0553: * @roller.wrapPojoMethod type="simple"
0554: * @ejb:persistent-field
0555: * @hibernate.property column="righttoleft" non-null="true" unique="false"
0556: */
0557: public Boolean getRightToLeft() {
0558: return rightToLeft;
0559: }
0560:
0561: /**
0562: * True if this entry should be rendered right to left.
0563: * @ejb:persistent-field
0564: */
0565: public void setRightToLeft(Boolean rightToLeft) {
0566: this .rightToLeft = rightToLeft;
0567: }
0568:
0569: /**
0570: * True if story should be pinned to the top of the Roller site main blog.
0571: * @return Returns the pinned.
0572: *
0573: * @roller.wrapPojoMethod type="simple"
0574: * @ejb:persistent-field
0575: * @hibernate.property column="pinnedtomain" non-null="true" unique="false"
0576: */
0577: public Boolean getPinnedToMain() {
0578: return pinnedToMain;
0579: }
0580:
0581: /**
0582: * True if story should be pinned to the top of the Roller site main blog.
0583: * @param pinnedToMain The pinned to set.
0584: *
0585: * @ejb:persistent-field
0586: */
0587: public void setPinnedToMain(Boolean pinnedToMain) {
0588: this .pinnedToMain = pinnedToMain;
0589: }
0590:
0591: /**
0592: * The locale string that defines the i18n approach for this entry.
0593: *
0594: * @roller.wrapPojoMethod type="simple"
0595: * @ejb:persistent-field
0596: * @hibernate.property column="locale" non-null="false" unique="false"
0597: */
0598: public String getLocale() {
0599: return locale;
0600: }
0601:
0602: public void setLocale(String locale) {
0603: this .locale = locale;
0604: }
0605:
0606: /**
0607: * @roller.wrapPojoMethod type="pojo-collection" class="org.apache.roller.pojos.WeblogEntryTagData"
0608: *
0609: * @ejb:persistent-field
0610: *
0611: * @hibernate.set lazy="true" order-by="name" inverse="true" cascade="all-delete-orphan"
0612: * @hibernate.collection-key column="entryid"
0613: * @hibernate.collection-one-to-many class="org.apache.roller.pojos.WeblogEntryTagData"
0614: */
0615: public Set getTags() {
0616: return tagSet;
0617: }
0618:
0619: private void setTags(Set tagSet) throws RollerException {
0620: this .tagSet = tagSet;
0621: this .removedTags = new HashSet();
0622: this .addedTags = new HashSet();
0623: }
0624:
0625: /**
0626: * Roller lowercases all tags based on locale because there's not a 1:1 mapping
0627: * between uppercase/lowercase characters across all languages.
0628: * @param name
0629: * @throws RollerException
0630: */
0631: public void addTag(String name) throws RollerException {
0632: Locale locale = getWebsite() != null ? getWebsite()
0633: .getLocaleInstance() : Locale.getDefault();
0634: name = Utilities.normalizeTag(name, locale);
0635: if (name.length() == 0)
0636: return;
0637:
0638: for (Iterator it = tagSet.iterator(); it.hasNext();) {
0639: WeblogEntryTagData tag = (WeblogEntryTagData) it.next();
0640: if (tag.getName().equals(name))
0641: return;
0642: }
0643:
0644: WeblogEntryTagData tag = new WeblogEntryTagData();
0645: tag.setName(name);
0646: tag.setUser(getCreator());
0647: tag.setWeblog(getWebsite());
0648: tag.setWeblogEntry(this );
0649: tag.setTime(getUpdateTime());
0650: tagSet.add(tag);
0651:
0652: addedTags.add(name);
0653: }
0654:
0655: public void removeTag(String name) throws RollerException {
0656: for (Iterator it = tagSet.iterator(); it.hasNext();) {
0657: WeblogEntryTagData tag = (WeblogEntryTagData) it.next();
0658: if (tag.getName().equals(name)) {
0659: removedTags.add(name);
0660: it.remove();
0661: }
0662: }
0663: }
0664:
0665: public Set getAddedTags() {
0666: return addedTags;
0667: }
0668:
0669: public Set getRemovedTags() {
0670: return removedTags;
0671: }
0672:
0673: public void updateTags(List tags) throws RollerException {
0674:
0675: if (tags == null)
0676: return;
0677:
0678: HashSet newTags = new HashSet(tags.size());
0679: Locale locale = getWebsite() != null ? getWebsite()
0680: .getLocaleInstance() : Locale.getDefault();
0681:
0682: for (Iterator it = tags.iterator(); it.hasNext();) {
0683: String name = (String) it.next();
0684: newTags.add(Utilities.normalizeTag(name, locale));
0685: }
0686:
0687: HashSet removeTags = new HashSet();
0688:
0689: // remove old ones no longer passed.
0690: for (Iterator it = tagSet.iterator(); it.hasNext();) {
0691: WeblogEntryTagData tag = (WeblogEntryTagData) it.next();
0692: if (!newTags.contains(tag.getName())) {
0693: removeTags.add(tag.getName());
0694: } else {
0695: newTags.remove(tag.getName());
0696: }
0697: }
0698:
0699: for (Iterator it = removeTags.iterator(); it.hasNext();) {
0700: removeTag((String) it.next());
0701: }
0702:
0703: for (Iterator it = newTags.iterator(); it.hasNext();) {
0704: addTag((String) it.next());
0705: }
0706: }
0707:
0708: /**
0709: * @roller.wrapPojoMethod type="simple"
0710: */
0711: public String getTagsAsString() {
0712: StringBuffer sb = new StringBuffer();
0713: for (Iterator it = getTags().iterator(); it.hasNext();) {
0714: sb.append(((WeblogEntryTagData) it.next()).getName())
0715: .append(" ");
0716: }
0717: if (sb.length() > 0) {
0718: sb.deleteCharAt(sb.length() - 1);
0719: }
0720:
0721: return sb.toString();
0722: }
0723:
0724: public void setTagsAsString(String tags) throws RollerException {
0725: if (tags == null) {
0726: tagSet.clear();
0727: return;
0728: }
0729:
0730: updateTags(Utilities.splitStringAsTags(tags));
0731: }
0732:
0733: // ------------------------------------------------------------------------
0734:
0735: /**
0736: * True if comments are still allowed on this entry considering the
0737: * allowComments and commentDays fields as well as the website and
0738: * site-wide configs.
0739: *
0740: * @roller.wrapPojoMethod type="simple"
0741: */
0742: public boolean getCommentsStillAllowed() {
0743: if (!RollerRuntimeConfig
0744: .getBooleanProperty("users.comments.enabled")) {
0745: return false;
0746: }
0747: if (website.getAllowComments() != null
0748: && !website.getAllowComments().booleanValue()) {
0749: return false;
0750: }
0751: if (getAllowComments() != null
0752: && !getAllowComments().booleanValue()) {
0753: return false;
0754: }
0755: boolean ret = false;
0756: if (getCommentDays() == null
0757: || getCommentDays().intValue() == 0) {
0758: ret = true;
0759: } else {
0760: // we want to use pubtime for calculating when comments expire, but
0761: // if pubtime isn't set (like for drafts) then just use updatetime
0762: Date pubTime = getPubTime();
0763: if (pubTime == null) {
0764: pubTime = getUpdateTime();
0765: }
0766:
0767: Calendar expireCal = Calendar.getInstance(getWebsite()
0768: .getLocaleInstance());
0769: expireCal.setTime(pubTime);
0770: expireCal.add(Calendar.DATE, getCommentDays().intValue());
0771: Date expireDay = expireCal.getTime();
0772: Date today = new Date();
0773: if (today.before(expireDay)) {
0774: ret = true;
0775: }
0776: }
0777: return ret;
0778: }
0779:
0780: public void setCommentsStillAllowed(boolean ignored) {
0781: // no-op
0782: }
0783:
0784: //------------------------------------------------------------------------
0785:
0786: /**
0787: * Format the publish time of this weblog entry using the specified pattern.
0788: * See java.text.SimpleDateFormat for more information on this format.
0789: *
0790: * @roller.wrapPojoMethod type="simple"
0791: * @see java.text.SimpleDateFormat
0792: * @return Publish time formatted according to pattern.
0793: */
0794: public String formatPubTime(String pattern) {
0795: try {
0796: SimpleDateFormat format = new SimpleDateFormat(pattern,
0797: this .getWebsite().getLocaleInstance());
0798:
0799: return format.format(getPubTime());
0800: } catch (RuntimeException e) {
0801: mLogger.error("Unexpected exception", e);
0802: }
0803:
0804: return "ERROR: formatting date";
0805: }
0806:
0807: //------------------------------------------------------------------------
0808:
0809: /**
0810: * Format the update time of this weblog entry using the specified pattern.
0811: * See java.text.SimpleDateFormat for more information on this format.
0812: *
0813: * @roller.wrapPojoMethod type="simple"
0814: * @see java.text.SimpleDateFormat
0815: * @return Update time formatted according to pattern.
0816: */
0817: public String formatUpdateTime(String pattern) {
0818: try {
0819: SimpleDateFormat format = new SimpleDateFormat(pattern);
0820:
0821: return format.format(getUpdateTime());
0822: } catch (RuntimeException e) {
0823: mLogger.error("Unexpected exception", e);
0824: }
0825:
0826: return "ERROR: formatting date";
0827: }
0828:
0829: //------------------------------------------------------------------------
0830:
0831: /**
0832: * @roller.wrapPojoMethod type="pojo-collection" class="org.apache.roller.pojos.CommentData"
0833: */
0834: public List getComments() {
0835: return getComments(true, true);
0836: }
0837:
0838: /**
0839: * @roller.wrapPojoMethod type="pojo-collection" class="org.apache.roller.pojos.CommentData"
0840: */
0841: public List getComments(boolean ignoreSpam, boolean approvedOnly) {
0842: List list = new ArrayList();
0843: try {
0844: WeblogManager wmgr = RollerFactory.getRoller()
0845: .getWeblogManager();
0846: return wmgr.getComments(getWebsite(), this , null, // search String
0847: null, // startDate
0848: null, // endDate
0849: null, // pending
0850: approvedOnly ? Boolean.TRUE : null, // approved
0851: ignoreSpam ? Boolean.FALSE : null, // spam
0852: false, // we want chrono order
0853: 0, // offset
0854: -1); // no limit
0855: } catch (RollerException alreadyLogged) {
0856: }
0857: return list;
0858: }
0859:
0860: /**
0861: * @roller.wrapPojoMethod type="simple"
0862: */
0863: public int getCommentCount() {
0864: List comments = getComments(true, true);
0865: return comments.size();
0866: }
0867:
0868: /** No-op to please XDoclet */
0869: public void setCommentCount(int ignored) {
0870: // no-op
0871: }
0872:
0873: //------------------------------------------------------------------------
0874:
0875: /**
0876: * @roller.wrapPojoMethod type="pojo-collection" class="org.apache.roller.pojos.RefererData"
0877: */
0878: public List getReferers() {
0879: List referers = null;
0880: try {
0881: referers = RollerFactory.getRoller().getRefererManager()
0882: .getReferersToEntry(getId());
0883: } catch (RollerException e) {
0884: mLogger.error("Unexpected exception", e);
0885: }
0886: return referers;
0887: }
0888:
0889: //------------------------------------------------------------------------
0890:
0891: /**
0892: * Returns absolute entry permalink.
0893: * @roller.wrapPojoMethod type="simple"
0894: */
0895: public String getPermalink() {
0896: return URLUtilities.getWeblogEntryURL(getWebsite(), null,
0897: anchor, true);
0898: }
0899:
0900: /**
0901: * Returns entry permalink, relative to Roller context.
0902: * @deprecated Use getPermalink() instead.
0903: * @roller.wrapPojoMethod type="simple"
0904: */
0905: public String getPermaLink() {
0906: String lAnchor = this .getAnchor();
0907: try {
0908: lAnchor = URLEncoder.encode(anchor, "UTF-8");
0909: } catch (UnsupportedEncodingException e) {
0910: // go with the "no encoding" version
0911: }
0912: WebsiteData website = this .getWebsite();
0913: return "/" + getWebsite().getHandle() + "/entry/" + lAnchor;
0914: }
0915:
0916: /**
0917: * Get relative URL to comments page.
0918: * @roller.wrapPojoMethod type="simple"
0919: * @deprecated Use commentLink() instead
0920: */
0921: public String getCommentsLink() {
0922: return getPermaLink() + "#comments";
0923: }
0924:
0925: /**
0926: * to please XDoclet
0927: * @deprecated Use commentLink() instead
0928: */
0929: public void setCommentsLink(String ignored) {
0930: }
0931:
0932: /**
0933: * Return the Title of this post, or the first 255 characters of the
0934: * entry's text.
0935: *
0936: * @roller.wrapPojoMethod type="simple"
0937: * @return String
0938: */
0939: public String getDisplayTitle() {
0940: if (getTitle() == null || getTitle().trim().equals("")) {
0941: return StringUtils.left(Utilities.removeHTML(text), 255);
0942: }
0943: return Utilities.removeHTML(getTitle());
0944: }
0945:
0946: //------------------------------------------------------------------------
0947:
0948: public String toString() {
0949: StringBuffer str = new StringBuffer("{");
0950:
0951: str.append("id=" + id + " " + "category=" + category + " "
0952: + "title=" + title + " " + "text=" + text + " "
0953: + "anchor=" + anchor + " " + "pubTime=" + pubTime + " "
0954: + "updateTime=" + updateTime + " " + "status=" + status
0955: + " " + "plugins=" + plugins);
0956: str.append('}');
0957:
0958: return (str.toString());
0959: }
0960:
0961: //------------------------------------------------------------------------
0962:
0963: public boolean equals(Object pOther) {
0964: if (pOther instanceof WeblogEntryData) {
0965: WeblogEntryData lTest = (WeblogEntryData) pOther;
0966: boolean lEquals = true;
0967:
0968: if (this .id == null) {
0969: lEquals = lEquals && (lTest.getId() == null);
0970: } else {
0971: lEquals = lEquals && this .id.equals(lTest.getId());
0972: }
0973:
0974: if (this .category == null) {
0975: lEquals = lEquals && (lTest.getCategory() == null);
0976: } else {
0977: lEquals = lEquals
0978: && this .category.equals(lTest.getCategory());
0979: }
0980:
0981: if (this .website == null) {
0982: lEquals = lEquals && (lTest.getWebsite() == null);
0983: } else {
0984: lEquals = lEquals
0985: && this .website.equals(lTest.getWebsite());
0986: }
0987:
0988: if (this .title == null) {
0989: lEquals = lEquals && (lTest.getTitle() == null);
0990: } else {
0991: lEquals = lEquals
0992: && this .title.equals(lTest.getTitle());
0993: }
0994:
0995: if (this .text == null) {
0996: lEquals = lEquals && (lTest.getText() == null);
0997: } else {
0998: lEquals = lEquals && this .text.equals(lTest.getText());
0999: }
1000:
1001: if (this .anchor == null) {
1002: lEquals = lEquals && (lTest.getAnchor() == null);
1003: } else {
1004: lEquals = lEquals
1005: && this .anchor.equals(lTest.getAnchor());
1006: }
1007:
1008: if (this .pubTime == null) {
1009: lEquals = lEquals && (lTest.getPubTime() == null);
1010: } else {
1011: lEquals = lEquals
1012: && this .pubTime.equals(lTest.getPubTime());
1013: }
1014:
1015: if (this .updateTime == null) {
1016: lEquals = lEquals && (lTest.getUpdateTime() == null);
1017: } else {
1018: lEquals = lEquals
1019: && this .updateTime
1020: .equals(lTest.getUpdateTime());
1021: }
1022:
1023: if (this .status == null) {
1024: lEquals = lEquals && (lTest.getStatus() == null);
1025: } else {
1026: lEquals = lEquals
1027: && this .status.equals(lTest.getStatus());
1028: }
1029:
1030: if (this .plugins == null) {
1031: lEquals = lEquals && (lTest.getPlugins() == null);
1032: } else {
1033: lEquals = lEquals
1034: && this .plugins.equals(lTest.getPlugins());
1035: }
1036:
1037: return lEquals;
1038: } else {
1039: return false;
1040: }
1041: }
1042:
1043: //------------------------------------------------------------------------
1044:
1045: public int hashCode() {
1046: int result = 17;
1047: result = (37 * result)
1048: + ((this .id != null) ? this .id.hashCode() : 0);
1049: result = (37 * result)
1050: + ((this .category != null) ? this .category.hashCode()
1051: : 0);
1052: result = (37 * result)
1053: + ((this .website != null) ? this .website.hashCode() : 0);
1054: result = (37 * result)
1055: + ((this .title != null) ? this .title.hashCode() : 0);
1056: result = (37 * result)
1057: + ((this .text != null) ? this .text.hashCode() : 0);
1058: result = (37 * result)
1059: + ((this .anchor != null) ? this .anchor.hashCode() : 0);
1060: result = (37 * result)
1061: + ((this .pubTime != null) ? this .pubTime.hashCode() : 0);
1062: result = (37 * result)
1063: + ((this .updateTime != null) ? this .updateTime
1064: .hashCode() : 0);
1065: result = (37 * result)
1066: + ((this .status != null) ? this .status.hashCode() : 0);
1067: result = (37 * result)
1068: + ((this .plugins != null) ? this .plugins.hashCode() : 0);
1069:
1070: return result;
1071: }
1072:
1073: /**
1074: * Return RSS 09x style description (escaped HTML version of entry text)
1075: *
1076: * @roller.wrapPojoMethod type="simple"
1077: */
1078: public String getRss09xDescription() {
1079: return getRss09xDescription(-1);
1080: }
1081:
1082: /**
1083: * Return RSS 09x style description (escaped HTML version of entry text)
1084: *
1085: * @roller.wrapPojoMethod type="simple"
1086: */
1087: public String getRss09xDescription(int maxLength) {
1088: String ret = StringEscapeUtils.escapeHtml(text);
1089: if (maxLength != -1 && ret.length() > maxLength) {
1090: ret = ret.substring(0, maxLength - 3) + "...";
1091: }
1092: return ret;
1093: }
1094:
1095: /** Create anchor for weblog entry, based on title or text */
1096: protected String createAnchor() throws RollerException {
1097: return RollerFactory.getRoller().getWeblogManager()
1098: .createAnchor(this );
1099: }
1100:
1101: /** Create anchor for weblog entry, based on title or text */
1102: public String createAnchorBase() {
1103:
1104: // Use title (minus non-alphanumeric characters)
1105: String base = null;
1106: if (!StringUtils.isEmpty(getTitle())) {
1107: base = Utilities.replaceNonAlphanumeric(getTitle(), ' ')
1108: .trim();
1109: }
1110: // If we still have no base, then try text (minus non-alphanumerics)
1111: if (StringUtils.isEmpty(base)
1112: && !StringUtils.isEmpty(getText())) {
1113: base = Utilities.replaceNonAlphanumeric(getText(), ' ')
1114: .trim();
1115: }
1116:
1117: if (!StringUtils.isEmpty(base)) {
1118:
1119: // Use only the first 4 words
1120: StringTokenizer toker = new StringTokenizer(base);
1121: String tmp = null;
1122: int count = 0;
1123: while (toker.hasMoreTokens() && count < 5) {
1124: String s = toker.nextToken();
1125: s = s.toLowerCase();
1126: tmp = (tmp == null) ? s : tmp + "_" + s;
1127: count++;
1128: }
1129: base = tmp;
1130: }
1131: // No title or text, so instead we will use the items date
1132: // in YYYYMMDD format as the base anchor
1133: else {
1134: base = DateUtil.format8chars(getPubTime());
1135: }
1136:
1137: return base;
1138: }
1139:
1140: /**
1141: * A no-op. TODO: fix formbean generation so this is not needed.
1142: */
1143: public void setPermalink(String string) {
1144: }
1145:
1146: /**
1147: * A no-op. TODO: fix formbean generation so this is not needed.
1148: */
1149: public void setPermaLink(String string) {
1150: }
1151:
1152: /**
1153: * A no-op.
1154: * TODO: fix formbean generation so this is not needed.
1155: * @param string
1156: */
1157: public void setDisplayTitle(String string) {
1158: }
1159:
1160: /**
1161: * A no-op.
1162: * TODO: fix formbean generation so this is not needed.
1163: * @param string
1164: */
1165: public void setRss09xDescription(String string) {
1166: }
1167:
1168: /**
1169: * Convenience method to transform mPlugins to a List
1170: *
1171: * @roller.wrapPojoMethod type="simple"
1172: * @return
1173: */
1174: public List getPluginsList() {
1175: if (plugins != null) {
1176: return Arrays.asList(StringUtils.split(plugins, ","));
1177: }
1178: return new ArrayList();
1179: }
1180:
1181: /**
1182: * Set creator by user id (for use in form's copyTo method)
1183: * @param creatorId
1184: */
1185: public void setCreatorId(String creatorId) throws RollerException {
1186: UserManager umgr = RollerFactory.getRoller().getUserManager();
1187: setCreator(umgr.getUser(creatorId));
1188: }
1189:
1190: /** Convenience method for checking status */
1191: public boolean isDraft() {
1192: return status.equals(DRAFT);
1193: }
1194:
1195: /** no-op: needed only to satisfy XDoclet, use setStatus() instead */
1196: public void setDraft(boolean value) {
1197: }
1198:
1199: /** Convenience method for checking status */
1200: public boolean isPending() {
1201: return status.equals(PENDING);
1202: }
1203:
1204: /** no-op: needed only to satisfy XDoclet, use setStatus() instead */
1205: public void setPending(boolean value) {
1206: }
1207:
1208: /** Convenience method for checking status */
1209: public boolean isPublished() {
1210: return status.equals(PUBLISHED);
1211: }
1212:
1213: /** no-op: needed only to satisfy XDoclet, use setStatus() instead */
1214: public void setPublished(boolean value) {
1215: }
1216:
1217: /**
1218: * Get entry text, transformed by plugins enabled for entry.
1219: * @roller.wrapPojoMethod type="simple"
1220: */
1221: public String getTransformedText() {
1222: return render(text);
1223: }
1224:
1225: /**
1226: * No-op to please XDoclet.
1227: */
1228: public void setTransformedText(String t) {
1229: // no-op
1230: }
1231:
1232: /**
1233: * Get entry summary, transformed by plugins enabled for entry.
1234: * @roller.wrapPojoMethod type="simple"
1235: */
1236: public String getTransformedSummary() {
1237: return render(summary);
1238: }
1239:
1240: /**
1241: * No-op to please XDoclet.
1242: */
1243: public void setTransformedSummary(String t) {
1244: // no-op
1245: }
1246:
1247: /**
1248: * Determine if the specified user has permissions to edit this entry.
1249: */
1250: public boolean hasWritePermissions(UserData user)
1251: throws RollerException {
1252:
1253: // global admins can hack whatever they want
1254: if (user.hasRole("admin")) {
1255: return true;
1256: }
1257:
1258: boolean author = getWebsite().hasUserPermissions(user,
1259: (short) (PermissionsData.AUTHOR));
1260: boolean limited = getWebsite().hasUserPermissions(user,
1261: (short) (PermissionsData.LIMITED));
1262:
1263: if (author || (limited && isDraft())
1264: || (limited && isPending())) {
1265: return true;
1266: }
1267:
1268: return false;
1269: }
1270:
1271: /**
1272: * Transform string based on plugins enabled for this weblog entry.
1273: */
1274: private String render(String str) {
1275: String ret = str;
1276: mLogger.debug("Applying page plugins to string");
1277: Map plugins = this .website.getInitializedPlugins();
1278: if (str != null && plugins != null) {
1279: List entryPlugins = getPluginsList();
1280:
1281: // if no Entry plugins, don't bother looping.
1282: if (entryPlugins != null && !entryPlugins.isEmpty()) {
1283:
1284: // now loop over mPagePlugins, matching
1285: // against Entry plugins (by name):
1286: // where a match is found render Plugin.
1287: Iterator iter = plugins.keySet().iterator();
1288: while (iter.hasNext()) {
1289: String key = (String) iter.next();
1290: if (entryPlugins.contains(key)) {
1291: WeblogEntryPlugin pagePlugin = (WeblogEntryPlugin) plugins
1292: .get(key);
1293: try {
1294: ret = pagePlugin.render(this , ret);
1295: } catch (Throwable t) {
1296: mLogger.error("ERROR from plugin: "
1297: + pagePlugin.getName(), t);
1298: }
1299: }
1300: }
1301: }
1302: }
1303: return ret;
1304: }
1305:
1306: /**
1307: * Get the right transformed display content depending on the situation.
1308: *
1309: * If the readMoreLink is specified then we assume the caller wants to
1310: * prefer summary over content and we include a "Read More" link at the
1311: * end of the summary if it exists. Otherwise, if the readMoreLink is
1312: * empty or null then we assume the caller prefers content over summary.
1313: *
1314: * @roller.wrapPojoMethod type="simple"
1315: */
1316: public String displayContent(String readMoreLink) {
1317:
1318: String displayContent = null;
1319:
1320: if (readMoreLink == null || readMoreLink.trim().length() < 1
1321: || "nil".equals(readMoreLink)) {
1322:
1323: // no readMore link means permalink, so prefer text over summary
1324: if (StringUtils.isNotEmpty(this .getText())) {
1325: displayContent = this .getTransformedText();
1326: } else {
1327: displayContent = this .getTransformedSummary();
1328: }
1329: } else {
1330: // not a permalink, so prefer summary over text
1331: // include a "read more" link if needed
1332: if (StringUtils.isNotEmpty(this .getSummary())) {
1333: displayContent = this .getTransformedSummary();
1334: if (StringUtils.isNotEmpty(this .getText())) {
1335: // add read more
1336: List args = new ArrayList();
1337: args.add(readMoreLink);
1338: String readMore = MessageUtilities.getString(
1339: "macro.weblog.readMoreLink", args);
1340:
1341: displayContent += readMore;
1342: }
1343: } else {
1344: displayContent = this .getTransformedText();
1345: }
1346: }
1347:
1348: return displayContent;
1349: }
1350:
1351: /**
1352: * Get the right transformed display content.
1353: *
1354: * @roller.wrapPojoMethod type="simple"
1355: */
1356: public String getDisplayContent() {
1357: return displayContent(null);
1358: }
1359:
1360: /** No-op method to please XDoclet */
1361: public void setDisplayContent(String ignored) {
1362: }
1363:
1364: }
|