diff --git a/querki/app/controllers/NavSection.scala b/querki/app/controllers/NavSection.scala index 1d81e42ce..f622e3d00 100644 --- a/querki/app/controllers/NavSection.scala +++ b/querki/app/controllers/NavSection.scala @@ -117,6 +117,12 @@ case class NavSections(sections:Seq[Navigable]) case class NavSection(val title:String, val links:Seq[Navigable]) extends Navigable +/** + * Represents a single link to be shown in a menu. + * + * IMPORTANT: the display is taken as literal HTML, and is not further escaped! Be very sure that anything + * you use for the display parameter has been properly HTML-neutered! + */ case class NavLink(display:String, url:Call, id:Option[String] = None, enabled:Boolean = true) extends Navigable case object NavDivider extends Navigable diff --git a/querki/app/models/Thing.scala b/querki/app/models/Thing.scala index 5e2651967..c6c224481 100644 --- a/querki/app/models/Thing.scala +++ b/querki/app/models/Thing.scala @@ -136,15 +136,35 @@ abstract class Thing( def thisAsContext(implicit request:RequestContext) = QLContext(ExactlyOne(LinkType(this.id)), Some(request)) - def displayName:String = { + /** + * The Display Name of this Thing, rendered as a String. + * + * IMPORTANT: what gets returned from here has already been HTML-processed, and should *not* + * be re-escaped! + */ + def displayName:String = displayNameText.toString + + /** + * The Display Name of this Thing. This is the underlying form of access, and should + * be used to get at it as Html or HtmlWikitext. It has already been HTML-neutered, and + * is the safest and most flexible way to use this name. + */ + def displayNameText:DisplayText = { val localName = localProp(DisplayNameProp) orElse localProp(NameProp) if (localName.isEmpty) - id.toString + DisplayText(id.toString) else { localName.get.renderPlain.raw - } + } } + /** + * The *literal* Display Name of this Thing, exactly as typed. + * + * IMPORTANT: this value has *NOT* been HTML-escaped. It must only be used in an environment that + * will do the escaping sometime later! Do not use this casually -- always test the environment that + * you will be using it in! + */ def unsafeDisplayName:String = { val localName = localProp(DisplayNameProp) orElse localProp(NameProp) if (localName.isEmpty) diff --git a/querki/app/models/Wikitext.scala b/querki/app/models/Wikitext.scala index 91c6b244c..1b443fe22 100644 --- a/querki/app/models/Wikitext.scala +++ b/querki/app/models/Wikitext.scala @@ -14,6 +14,9 @@ case class DisplayText(val str:String) { override def toString() = str def +(other:DisplayText) = new DisplayText(str + other.str) + // Since a DisplayText is already HTML-neutered, it is safe to encode as HTML: + def html = Html(str) + def htmlWikitext = HtmlWikitext(html) } object DisplayText { implicit def displayText2String(disp:DisplayText) = disp.str diff --git a/querki/app/models/system/Types.scala b/querki/app/models/system/Types.scala index 48450cf47..a6cc6d7d5 100644 --- a/querki/app/models/system/Types.scala +++ b/querki/app/models/system/Types.scala @@ -399,7 +399,7 @@ object QLType extends QLType(QLTypeOID) val target = follow(context)(v) val text = target match { case Some(t) => { - val display = displayOpt.getOrElse(Wikitext(t.displayName)) + val display = displayOpt.getOrElse(t.displayNameText.htmlWikitext) makeWikiLink(context, t, display) } case None => Wikitext("Bad Link: Thing " + v.toString + " not found") diff --git a/querki/app/querki/search/Search.scala b/querki/app/querki/search/Search.scala index 87d7fecf3..07f8b2224 100644 --- a/querki/app/querki/search/Search.scala +++ b/querki/app/querki/search/Search.scala @@ -25,8 +25,8 @@ object Search { // *rendered* view of this Thing. But that's challenging, so this should probably become Can View Source for now. if (!space.canRead(requester, t.id)) Seq() - else if (t.displayName.toLowerCase().contains(searchComp)) { - Seq(SearchResult(t, DisplayNameProp, 1.0, DisplayText(t.displayName), List(t.displayName.toLowerCase().indexOf(searchComp)))) + else if (t.unsafeDisplayName.toLowerCase().contains(searchComp)) { + Seq(SearchResult(t, DisplayNameProp, 1.0, t.unsafeDisplayName, List(t.unsafeDisplayName.toLowerCase().indexOf(searchComp)))) } else { // For now, we're only going to deal with Text types. // TODO: cope with Links, Tags, and things like that! diff --git a/querki/app/querki/search/SearchResults.scala b/querki/app/querki/search/SearchResults.scala index 794dd7faa..377540e5e 100644 --- a/querki/app/querki/search/SearchResults.scala +++ b/querki/app/querki/search/SearchResults.scala @@ -2,6 +2,10 @@ package querki.search import models._ +/** + * A single result from searching. These results are *not* HTML-neutered, so they must be escaped + * before rendering! + */ case class SearchResult(thing:Thing, prop:Property[_,_], score:Double, text:String, positions:List[Int]) case class SearchResults(request:String, results:Seq[SearchResult]) \ No newline at end of file diff --git a/querki/app/views/editThing.scala.html b/querki/app/views/editThing.scala.html index 592cfa9fd..f195486f8 100644 --- a/querki/app/views/editThing.scala.html +++ b/querki/app/views/editThing.scala.html @@ -73,7 +73,7 @@ @setTooltip(prop:Property[_,_]) = { @defining(prop.getPropOpt(PropSummary)(space)) { summaryOpt => - @if(summaryOpt.isDefined) { title="@{summaryOpt.get.render(prop.thisAsContext(rc)).raw}" } + @if(summaryOpt.isDefined) { title="@Html(summaryOpt.get.render(prop.thisAsContext(rc)).raw)" } } } @@ -81,7 +81,7 @@
@if(!canEdit) { diff --git a/querki/app/views/main.scala.html b/querki/app/views/main.scala.html index 19bada846..4998800a6 100644 --- a/querki/app/views/main.scala.html +++ b/querki/app/views/main.scala.html @@ -71,9 +71,9 @@ @displayNavLink(display:String, url:Call, idStr:String, enabled:Boolean) = { @if(enabled) { -
  • @display
  • +
  • @Html(display)
  • } else { -
  • @display
  • +
  • @Html(display)
  • } } @@ -123,7 +123,7 @@ - @title + @Html(title) @@ -328,7 +328,7 @@ @if(thing.isDefined) {
    -

    Are you sure you want to delete @{thing.get.displayName}? There is currently no way to get it back!

    +

    Are you sure you want to delete @Html(thing.get.displayName)? There is currently no way to get it back!

    } diff --git a/querki/app/views/searchResults.scala.html b/querki/app/views/searchResults.scala.html index ab223813e..a4f3aa1b8 100644 --- a/querki/app/views/searchResults.scala.html +++ b/querki/app/views/searchResults.scala.html @@ -39,7 +39,7 @@

    @title

    @for(result <- results.results.sortBy(_.score).reverse) { -
    @result.thing.displayName (@result.prop.displayName)
    +
    @result.thing.displayNameText.html (@result.prop.displayNameText.html)
    @boldfaceResult(result)
    }
    diff --git a/querki/app/views/showPropertyTemplate.scala.html b/querki/app/views/showPropertyTemplate.scala.html index 21a53d6c4..722b162dc 100644 --- a/querki/app/views/showPropertyTemplate.scala.html +++ b/querki/app/views/showPropertyTemplate.scala.html @@ -19,7 +19,7 @@