/*
* Copyright (c) 2006-2013 by Public Library of Science
*
* http://plos.org
* http://ambraproject.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.ambraproject.action.article;
import com.opensymphony.xwork2.validator.annotations.RequiredStringValidator;
import org.ambraproject.ApplicationException;
import org.ambraproject.action.BaseSessionAwareActionSupport;
import org.ambraproject.models.Article;
import org.ambraproject.service.article.NoSuchObjectIdException;
import org.ambraproject.views.CitationView;
import org.ambraproject.service.captcha.CaptchaService;
import org.ambraproject.web.Cookies;
import org.ambraproject.freemarker.AmbraFreemarkerConfig;
import org.ambraproject.models.AnnotationType;
import org.ambraproject.models.ArticleView;
import org.ambraproject.models.UserProfile;
import org.ambraproject.service.annotation.AnnotationService;
import org.ambraproject.service.article.ArticleAssetService;
import org.ambraproject.service.article.ArticleAssetWrapper;
import org.ambraproject.service.article.ArticleService;
import org.ambraproject.service.article.FetchArticleService;
import org.ambraproject.service.article.NoSuchArticleIdException;
import org.ambraproject.service.trackback.TrackbackService;
import org.ambraproject.service.user.UserService;
import org.ambraproject.util.TextUtils;
import org.ambraproject.util.UriUtil;
import org.ambraproject.views.AnnotationView;
import org.ambraproject.views.ArticleCategory;
import org.ambraproject.views.AuthorView;
import org.ambraproject.views.CitationReference;
import org.ambraproject.views.JournalView;
import org.ambraproject.views.article.ArticleInfo;
import org.ambraproject.views.article.ArticleType;
import org.ambraproject.views.article.RelatedArticleInfo;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import org.w3c.dom.Document;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static org.ambraproject.service.annotation.AnnotationService.AnnotationOrder;
/**
* This class fetches the information from the service tier for the article Tabs. Common data is defined in the
* setCommonData. One method is defined for each tab.
* <p/>
* Freemarker builds rest like URLs, inbound and outbound as defined in the /WEB-INF/urlrewrite.xml file. These URLS
* then map to the methods are referenced in the struts.xml file.
* <p/>
* ex: http://localhost/article/related/info%3Adoi%2F10.1371%2Fjournal.pone.0299
* <p/>
* Gets rewritten to:
* <p/>
* http://localhost/fetchArticleRelated.action&articleURI=info%3Adoi%2F10.1371%2Fjournal.pone.0299
* <p/>
* Struts picks this up and translates it call the FetchArticleRelated method ex: <action name="fetchRelatedArticle"
* class="org.ambraproject.article.action.FetchArticleTabsAction" method="FetchArticleRelated">
*/
public class FetchArticleTabsAction extends BaseSessionAwareActionSupport implements ArticleHeaderAction {
private static final Logger log = LoggerFactory.getLogger(FetchArticleTabsAction.class);
private final ArrayList<String> messages = new ArrayList<String>();
private static final int RELATED_AUTHOR_SEARCH_QUERY_SIZE = 4;
/**
* Returned by fetchArticle() when the given DOI is not in the repository.
*/
public static final String ARTICLE_NOT_FOUND = "articleNotFound";
public static final String EXPRESSION_OF_CONCERN_RELATION = "expressed-concern";
public static final String CORRECTION_RELATION = "correction-forward";
public static final String RETRACTION_RELATION = "retraction";
private String articleURI;
private String transformedArticle;
private String annotationId = "";
private String retraction = "";
private String expressionOfConcern = "";
private CitationView retractionCitation;
private CitationView eocCitation;
private List<CitationView> articleCorrection = new ArrayList<CitationView>();
private List<String> correspondingAuthor;
private List<String> authorContributions;
private List<String> competingInterest;
private int pageCount = 0;
private int numComments = 0;
//commentary holds the comments that are being listed
private AnnotationView[] commentary = new AnnotationView[0];
private boolean isResearchArticle;
private String publishedJournal = "";
private ArticleInfo articleInfoX;
private Document doc;
private ArticleType articleType;
private List<List<String>> articleIssues;
private int trackbackCount;
private List<AuthorView> authors;
private List<CitationReference> references;
private String journalAbbrev;
private String relatedAuthorSearchQuery;
private Set<JournalView> journalList;
private ArticleAssetWrapper[] articleAssetWrapper;
private AmbraFreemarkerConfig ambraFreemarkerConfig;
private FetchArticleService fetchArticleService;
private AnnotationService annotationService;
private ArticleService articleService;
private TrackbackService trackbackService;
private UserService userService;
private ArticleAssetService articleAssetService;
private Set<ArticleCategory> categories;
private CaptchaService captchaService;
private UserProfile user;
private String reCaptchaPublicKey;
private boolean hasPDF;
/**
* Fetch the data for Article Tab
*
* @return "success" on success, "error" on error
*/
public String fetchArticle() throws Exception {
try {
setCommonData();
articleAssetWrapper = articleAssetService.listFiguresTables(articleInfoX.getDoi(), getAuthId());
fetchAmendment();
transformedArticle = fetchArticleService.getArticleAsHTML(articleInfoX);
} catch (NoSuchArticleIdException e) {
messages.add("No article found for id: " + articleURI);
log.info("Could not find article: " + articleURI, e);
return ARTICLE_NOT_FOUND;
}
recordArticleView();
return SUCCESS;
}
/**
* check if the article has any amendments; if the article has eoc or retraction, fetch the body and citation.
* If the article has only corrections fetch the citations.
*
* @return String
*/
private String fetchAmendment() {
try {
if (articleInfoX.getRelatedArticles() != null
&& !articleService.isCorrectionArticle(articleInfoX)
&& !articleService.isRetractionArticle(articleInfoX)
&& !articleService.isEocArticle(articleInfoX)) {
for (RelatedArticleInfo relatedArticleInfo : articleInfoX.getRelatedArticles()) {
if ((relatedArticleInfo.getArticleTypes() != null)) {
// currently, we don't have many related articles; therefore, these lines
// shouldn't cause performance overhead.
Article article = articleService.getArticle(relatedArticleInfo.getDoi(), getAuthId());
ArticleInfo articleInfo = articleService.getArticleInfo(relatedArticleInfo.getDoi(), getAuthId());
Document document = this.fetchArticleService.getArticleDocument(articleInfo);
if (RETRACTION_RELATION.equalsIgnoreCase(relatedArticleInfo.getRelationType()) &&
articleService.isRetractionArticle(relatedArticleInfo)) {
retraction = this.fetchArticleService.getAmendmentBody(document);
retractionCitation = buildCitationFromArticle(article);
break;
}
if (EXPRESSION_OF_CONCERN_RELATION.equalsIgnoreCase(relatedArticleInfo.getRelationType()) &&
articleService.isEocArticle(relatedArticleInfo)) {
expressionOfConcern = this.fetchArticleService.getAmendmentBody(document);
eocCitation = buildCitationFromArticle(article);
break;
}
if (CORRECTION_RELATION.equalsIgnoreCase(relatedArticleInfo.getRelationType()) &&
articleService.isCorrectionArticle(relatedArticleInfo)) {
CitationView citation = buildCitationFromArticle(article);
articleCorrection.add(citation);
}
}
}
}
} catch (Exception e) {
populateErrorMessages(e);
return ERROR;
}
return SUCCESS;
}
private CitationView buildCitationFromArticle(Article article) {
CitationView citation = CitationView.builder()
.setDoi(article.getDoi())
.seteLocationId(article.geteLocationId())
.setUrl(article.getUrl())
.setTitle(article.getTitle())
.setJournal(article.getJournal())
.setVolume(article.getVolume())
.setIssue(article.getIssue())
.setSummary(article.getDescription())
.setPublisherName(article.getPublisherName())
.setPublishedDate(article.getDate())
.setAuthorList(article.getAuthors())
.setCollaborativeAuthors(article.getCollaborativeAuthors())
.build();
return citation;
}
/**
* Fetch data for Comments Tab
*
* @return "success" on success, "error" on error
*/
public String fetchArticleComments() {
try {
setCommonData();
numComments = annotationService.countAnnotations(articleInfoX.getId(),
EnumSet.of(AnnotationType.COMMENT));
} catch (Exception e) {
populateErrorMessages(e);
return ERROR;
}
return SUCCESS;
}
/**
* Fetches data for Authors Tab
*
* @return "success" on success, "error" on error
*/
public String fetchArticleAuthors() {
try {
setCommonData();
} catch (Exception e) {
populateErrorMessages(e);
}
return SUCCESS;
}
/**
* Fetches data for Metrics Tab
*
* @return "success" on success, "error" on error
*/
public String fetchArticleMetrics() {
try {
setCommonData();
trackbackCount = trackbackService.countTrackbacksForArticle(articleURI);
//count all the comments
numComments = annotationService.countAnnotations(articleInfoX.getId(),
EnumSet.of(AnnotationType.COMMENT));
} catch (Exception e) {
populateErrorMessages(e);
return ERROR;
}
return SUCCESS;
}
/**
* Fetches data for Related Content Tab
*
* @return "success" on success, "error" on error
*/
public String fetchArticleRelated() {
try {
setCommonData();
populateRelatedAuthorSearchQuery();
user = getCurrentUser();
reCaptchaPublicKey = captchaService.getPublicKey();
} catch (Exception e) {
populateErrorMessages(e);
}
return SUCCESS;
}
/** This method gets called when user click on crossref tile inside metrics tab
* Fetches common data and nothing else
*
* @return "success" on succes, "error" on error
*/
public String fetchArticleCrossRef() {
try {
setCommonData();
} catch (NoSuchArticleIdException e) {
messages.add("No article found for id: " + articleURI);
log.info("Could not find article: " + articleURI, e);
return ERROR;
} catch (Exception e) {
messages.add(e.getMessage());
log.error("Error retrieving article: " + articleURI, e);
return ERROR;
}
return SUCCESS;
}
/**
* Sets up data used by the right hand column in the freemarker templates
* <p/>
* TODO: Review the data fetched by this; it's called on every tab and fetches more than is necessary (e.g.
* articleInfo)
*
* @throws ApplicationException when there is an error talking to the OTM
* @throws NoSuchArticleIdException when the article can not be found
*/
private void setCommonData() throws ApplicationException, NoSuchArticleIdException, NoSuchObjectIdException {
validateArticleURI();
articleInfoX = articleService.getArticleInfo(articleURI, getAuthId());
hasPDF = true;
if (articleAssetService.getArticleAsset(articleURI, "PDF", getAuthId()) == null) {
hasPDF = false;
}
// sort the related articles by date
Collections.sort(articleInfoX.getRelatedArticles());
journalList = articleInfoX.getJournals();
isResearchArticle = articleService.isResearchArticle(articleInfoX);
articleIssues = articleService.getArticleIssues(articleURI);
articleType = articleInfoX.getKnownArticleType();
String pages = this.articleInfoX.getPages();
if (pages != null && pages.indexOf("-") > 0 && pages.split("-").length > 1) {
String t = pages.split("-")[1];
try {
pageCount = Integer.parseInt(t);
} catch (NumberFormatException ex) {
log.warn("Not able to parse page count from citation pages property with value of (" + t + ")");
pageCount = 0;
}
}
//TODO: Refactor this to not be spaghetti, all these properties should be made
//to be part of articleInfo. Rename articleInfo to articleView and populate articleView
//In the service tier in whatever way is appropriate
doc = this.fetchArticleService.getArticleDocument(articleInfoX);
authors = this.fetchArticleService.getAuthors(doc);
correspondingAuthor = this.fetchArticleService.getCorrespondingAuthors(doc);
authorContributions = this.fetchArticleService.getAuthorContributions(doc);
competingInterest = this.fetchArticleService.getAuthorCompetingInterests(doc);
references = this.fetchArticleService.getReferences(doc);
journalAbbrev = this.fetchArticleService.getJournalAbbreviation(doc);
commentary = this.annotationService.listAnnotations(articleInfoX.getId(),
EnumSet.of(AnnotationType.COMMENT),
AnnotationOrder.MOST_RECENT_REPLY);
/**
An article can be cross published, but we want the source journal.
If in this collection an article eIssn matches the article's eIssn keep that value.
freemarker_config.getDisplayName(journalContext)}">
**/
for (JournalView j : journalList) {
if (articleInfoX.geteIssn().equals(j.geteIssn())) {
publishedJournal = ambraFreemarkerConfig.getDisplayName(j.getJournalKey());
}
}
this.categories = Cookies.setAdditionalCategoryFlags(articleInfoX.getCategories(), articleInfoX.getId());
}
@Override
public boolean getHasAboutAuthorContent() {
return authors != null ? AuthorView.anyHasAffiliation(authors)
|| CollectionUtils.isNotEmpty(correspondingAuthor)
|| CollectionUtils.isNotEmpty(authorContributions)
|| CollectionUtils.isNotEmpty(competingInterest) : false;
}
/**
* This method is called only when request has x-pjax in its header. Rule is defined
* in urlrewrite.xml
*
* Fetch data for Article tab
*
* @return "success" on success, "error" on error
*/
public String fetchArticleContent() throws Exception {
try {
validateArticleURI();
articleInfoX = articleService.getArticleInfo(articleURI, getAuthId());
articleAssetWrapper = articleAssetService.listFiguresTables(articleInfoX.getDoi(), getAuthId());
commentary = annotationService.listAnnotations(
articleInfoX.getId(),
EnumSet.of(AnnotationType.COMMENT),
AnnotationOrder.MOST_RECENT_REPLY);
fetchAmendment();
transformedArticle = fetchArticleService.getArticleAsHTML(articleInfoX);
} catch (Exception e) {
populateErrorMessages(e);
return (e instanceof NoSuchArticleIdException ? ARTICLE_NOT_FOUND : ERROR);
}
//If the user is logged in, record this as an article view
recordArticleView();
return SUCCESS;
}
/**
* This method is called only when request has x-pjax in its header. Rule is defined
* in urlrewrite.xml
*
* Fetch data for Comments Tab
*
* @return "success" on success, "error" on error
*/
public String fetchArticleCommentsContent() {
try {
validateArticleURI();
articleInfoX = articleService.getArticleInfo(articleURI, getAuthId());
commentary = annotationService.listAnnotations(
articleInfoX.getId(),
EnumSet.of(AnnotationType.COMMENT),
AnnotationOrder.MOST_RECENT_REPLY);
} catch (Exception e) {
populateErrorMessages(e);
return ERROR;
}
return SUCCESS;
}
/**
* This method is called only when request has x-pjax in its header. Rule is defined
* in urlrewrite.xml
*
* Fetches data for Authors Tab
*
* @return "success" on success, "error" on error
*/
public String fetchArticleAuthorsContent() {
try {
validateArticleURI();
articleInfoX = articleService.getArticleInfo(articleURI, getAuthId());
doc = this.fetchArticleService.getArticleDocument(articleInfoX);
authors = this.fetchArticleService.getAuthors(doc);
correspondingAuthor = this.fetchArticleService.getCorrespondingAuthors(doc);
authorContributions = this.fetchArticleService.getAuthorContributions(doc);
competingInterest = this.fetchArticleService.getAuthorCompetingInterests(doc);
} catch (Exception e) {
populateErrorMessages(e);
return ERROR;
}
return SUCCESS;
}
/**
* This method is called only when request has x-pjax in its header. Rule is defined
* in urlrewrite.xml
*
* Fetches data for Metrics Tab
*
* @return "success" on success, "error" on error
*/
public String fetchArticleMetricsContent() {
try {
validateArticleURI();
articleInfoX = articleService.getArticleInfo(articleURI, getAuthId());
numComments = annotationService.countAnnotations(articleInfoX.getId(),
EnumSet.of(AnnotationType.COMMENT));
trackbackCount = trackbackService.countTrackbacksForArticle(articleURI);
} catch (Exception e) {
populateErrorMessages(e);
return ERROR;
}
return SUCCESS;
}
/**
* This method is called only when request has x-pjax in its header. Rule is defined
* in urlrewrite.xml
*
* Fetches data for Related Content Tab
*
* @return "success" on success, "error" on error
*/
public String fetchArticleRelatedContent() {
try {
validateArticleURI();
articleInfoX = articleService.getArticleInfo(articleURI, getAuthId());
populateRelatedAuthorSearchQuery();
user = getCurrentUser();
reCaptchaPublicKey = captchaService.getPublicKey();
} catch (Exception e) {
populateErrorMessages(e);
return ERROR;
}
return SUCCESS;
}
/**
* validate the article URI
* @throws NoSuchArticleIdException
*/
private void validateArticleURI() throws NoSuchArticleIdException {
try {
UriUtil.validateUri(articleURI, "articleURI=<" + articleURI + ">");
} catch (Exception e) {
throw new NoSuchArticleIdException(articleURI, e.getMessage(), e);
}
}
/**
* populate the error messages
* @param e
*/
private void populateErrorMessages(Exception e) {
if (e instanceof NoSuchArticleIdException) {
messages.add("No article found for id: " + articleURI);
log.info("Could not find article: " + articleURI, e);
}
else {
messages.add(e.getMessage());
log.error("Error retrieving article: " + articleURI, e);
}
}
/**
* populate the author search query
*/
private void populateRelatedAuthorSearchQuery() {
// get the first two and the last two authors
List<String> authors = articleInfoX.getAuthors();
int authorSize = authors.size();
relatedAuthorSearchQuery = "";
if (authorSize <= RELATED_AUTHOR_SEARCH_QUERY_SIZE) {
for (String author : authors) {
relatedAuthorSearchQuery = relatedAuthorSearchQuery + "\"" + author + "\" OR ";
}
// remove the last ", OR "
if (relatedAuthorSearchQuery.length() > 0) {
relatedAuthorSearchQuery = relatedAuthorSearchQuery.substring(0, relatedAuthorSearchQuery.length() - 4);
}
} else {
// get first 2
relatedAuthorSearchQuery = "\"" + authors.get(0) + "\" OR ";
relatedAuthorSearchQuery = relatedAuthorSearchQuery + "\"" + authors.get(1) + "\" OR ";
// get last 2
relatedAuthorSearchQuery = relatedAuthorSearchQuery + "\"" + authors.get(authorSize - 2) + "\" OR ";
relatedAuthorSearchQuery = relatedAuthorSearchQuery + "\"" + authors.get(authorSize - 1) + "\"";
}
}
/**
* record the article view
*/
private void recordArticleView() {
//If the user is logged in, record this as an article view
UserProfile user = getCurrentUser();
if (user != null) {
try {
userService.recordArticleView(user.getID(), articleInfoX.getId(), ArticleView.Type.ARTICLE_VIEW);
} catch (Exception e) {
log.error("Error recording an article view for user: {} and article: {}", user.getID(), articleInfoX.getId());
log.error(e.getMessage(), e);
}
}
}
/**
* Set the fetch article service
*
* @param articleService articleService
*/
@Required
public void setArticleService(ArticleService articleService) {
this.articleService = articleService;
}
/**
* Set the fetch article service
*
* @param fetchArticleService fetchArticleService
*/
@Required
public void setFetchArticleService(final FetchArticleService fetchArticleService) {
this.fetchArticleService = fetchArticleService;
}
/**
* @param trackBackservice The trackBackService to set.
*/
@Required
public void setTrackBackService(TrackbackService trackBackservice) {
this.trackbackService = trackBackservice;
}
/**
* @param annotationService The annotationService to set.
*/
@Required
public void setAnnotationService(AnnotationService annotationService) {
this.annotationService = annotationService;
}
/**
* @param articleAssetService the articleAssetService
*/
public void setArticleAssetService(ArticleAssetService articleAssetService) {
this.articleAssetService = articleAssetService;
}
/**
* @return articleURI
*/
@RequiredStringValidator(message = "Article URI is required.")
public String getArticleURI() {
return articleURI;
}
/**
* Set articleURI to fetch the article for.
*
* @param articleURI articleURI
*/
public void setArticleURI(final String articleURI) {
this.articleURI = articleURI;
}
/**
* @return transformed output
*/
public String getTransformedArticle() {
return transformedArticle;
}
/**
* Return the ArticleInfo from the Browse cache.
* <p/>
*
* @return Returns the articleInfoX.
*/
public ArticleInfo getArticleInfoX() {
return articleInfoX;
}
/**
* @return the articleAssetWrapper
*/
public ArticleAssetWrapper[] getArticleAssetWrapper() {
return articleAssetWrapper;
}
/**
* @return Returns the annotationId.
*/
public String getAnnotationId() {
return annotationId;
}
/**
* @param annotationId The annotationId to set.
*/
public void setAnnotationId(String annotationId) {
this.annotationId = annotationId;
}
/**
* Gets the article Type
*
* @return Returns articleType
*/
public ArticleType getArticleType() {
return articleType;
}
public ArrayList<String> getMessages() {
return messages;
}
/**
* @return Returns the journalList.
*/
public Set<JournalView> getJournalList() {
return journalList;
}
/**
* @return Returns the names and URIs of all of the Journals, Volumes, and Issues to which this Article has been
* attached. This includes "collections", but does not include the
*/
public List<List<String>> getArticleIssues() {
return articleIssues;
}
/**
* @return the isResearchArticle
*/
public boolean getIsResearchArticle() {
return isResearchArticle;
}
public int getTrackbackCount() {
return trackbackCount;
}
public String getPublishedJournal() {
return publishedJournal;
}
/**
* If available, return the current count of pages.
*
* @return the current article's page count
*/
public int getPageCount() {
return pageCount;
}
/**
* Return a comma delimited string of authors.
*
* @return Comma delimited string of authors
*/
public String getAuthorNames() {
return AuthorView.buildNameList(authors);
}
/**
* Get the corresponding author
*
* @return
*/
public List<String> getCorrespondingAuthor() {
return this.correspondingAuthor;
}
/**
* Get the author contributions
*
* @return
*/
public List<String> getAuthorContributions() {
return this.authorContributions;
}
/**
* Get the authors competing interest
*
* @return
*/
public List<String> getCompetingInterest() {
return competingInterest;
}
/**
*
* @return body test of the article's retraction
*/
public String getRetraction() {
return this.retraction;
}
/**
*
* @return the citation for the article's retraction
*/
public CitationView getRetractionCitation() {
return retractionCitation;
}
/**
*
* @return the body text of Expression Of Concern
*/
public String getExpressionOfConcern() {
return expressionOfConcern;
}
public void setExpressionOfConcern(String expressionOfConcern) {
this.expressionOfConcern = expressionOfConcern;
}
/**
*
* @return the citation of the article's expression of concern
*/
public CitationView getEocCitation() {
return eocCitation;
}
/**
*
* @return article corrections
*/
public List<CitationView> getArticleCorrection() {
return articleCorrection;
}
public void setArticleCorrection(List<CitationView> articleCorrection) {
this.articleCorrection = articleCorrection;
}
public int getNumComments() {
return numComments;
}
public AnnotationView[] getCommentary() {
return commentary;
}
/**
* Return a list of this article's categories.
*
* Note: These values may be different pending the user's cookies then the values stored in the database.
*
* If a user is logged in, a list is built of categories(and if they have been flagged) for the article
* from the database
*
* If a user is not logged in, a list is built of categories for the article. Then we append (from a cookie)
* flagged categories for this article
*
* @return Return a list of this article's categories
*/
public Set<ArticleCategory> getCategories() {
return categories;
}
/**
* Set the config class containing all of the properties used by the Freemarker templates so those values can be used
* within this Action class.
*
* @param ambraFreemarkerConfig All of the configuration properties used by the Freemarker templates
*/
@Required
public void setAmbraFreemarkerConfig(final AmbraFreemarkerConfig ambraFreemarkerConfig) {
this.ambraFreemarkerConfig = ambraFreemarkerConfig;
}
@Required
public void setUserService(UserService userService) {
this.userService = userService;
}
/**
* Returns a list of author affiliations
*
* @return author affiliations
*/
public List<AuthorView> getAuthors() {
return this.authors;
}
/**
* Get a list of contributing authors
*
* @return
*/
public String getContributingAuthors() {
return AuthorView.buildContributingAuthorsList(authors);
}
/**
* Generate a list of authors grouped by affiliation
*
* @return a list of authors grouped by affiliation and sorted according to the order of the institutions in the xml
*
*/
public Set<Map.Entry<String, List<AuthorView>>> getAuthorsByAffiliation() throws RuntimeException{
return fetchArticleService.getAuthorsByAffiliation(doc, authors).entrySet();
}
/**
* Returns a list of citation references
*
* @return citation references
*/
public List<CitationReference> getReferences() {
return this.references;
}
/**
* Returns abbreviated journal name
*
* @return abbreviated journal name
*/
public String getJournalAbbrev() {
return this.journalAbbrev;
}
/**
* Returns article description
* <p/>
* //TODO: This is a pretty heavy weight function that gets called for every article request to get a value that
* rarely changes. Should we just make the value in the database correct on ingest?
*
* @return
*/
public String getArticleDescription() {
return TextUtils.transformXMLtoHtmlText(articleInfoX.getDescription());
}
/**
* Returns related article author search query
*
* @return author name query
*/
public String getRelatedAuthorSearchQuery() {
return relatedAuthorSearchQuery;
}
public UserProfile getUser() {
return user;
}
public String getReCaptchaPublicKey() {
return reCaptchaPublicKey;
}
@Required
public void setCaptchaService(CaptchaService captchaService) {
this.captchaService = captchaService;
}
public boolean isHasPDF() {
return hasPDF;
}
}