Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
357 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
dl { | ||
margin-right: 2em; | ||
width: 500px; | ||
} | ||
dt { | ||
font-weight: bold; | ||
font-size: 0.9em; | ||
margin-bottom: 0.7em; | ||
} | ||
dd { | ||
background:none no-repeat left top; | ||
color: #444; | ||
font-size: 0.9em; | ||
line-height: 1.5em; | ||
margin: 0 0 2em; | ||
padding-left: 4.5em; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
<h1>SocialMetadata module</h1> | ||
<p>The <code>SocialMetadata</code> module provides information in meta tags to improve social media, and | ||
search engine crawls. It is separated into sections to target specific consumers. | ||
</p> | ||
<dl> | ||
<dt>Base data</dt> | ||
<dd>Base data are basic meta tags that are typically used by search engines and social media sites that do not | ||
require specially composed tags. The <em>description</em> and <em>keywords</em> are particularly important. | ||
<dl> | ||
<dt>Description</dt> | ||
<dd>Description of the page content.</dd> | ||
<dt>Keywords</dt> | ||
<dd>Keywords that may be used to find the page.</dd> | ||
<dt>Author</dt> | ||
<dd>Author of the page content. Particularly useful when the page represents an article written by a | ||
recognizable author.</dd> | ||
<dt>Copyright</dt> | ||
<dd>When it is important to specify the copyright.</dd> | ||
<dt>Application-name</dt> | ||
<dd>The name of the application where the page resides.</dd> | ||
</dl> | ||
There is also an <code>extras</code> parameter to permit one to add extra parameters. | ||
</dd> | ||
<dt>Twitter</dt> | ||
<dd>Twitter data are used to generate a twitter | ||
<a href="https://dev.twitter.com/cards/types/summary" target="_blank">summary card</a>. | ||
This permits twitter to present a preview for a shared link. The meta data can be tested using the | ||
<a href="https://cards-dev.twitter.com/validator" target="_blank">card validator</a>. | ||
<dl> | ||
<dt>Title</dt> | ||
<dd>The title of the content for the page. Likely should match the <code>title</code> tag designated in the | ||
<code>head</code> of the page, though this should be 70 characters or fewer; the preview will truncate the | ||
title at 70 characters.</dd> | ||
<dt>Description</dt> | ||
<dd>A 140 character description of the page content.</dd> | ||
<dt>Image</dt> | ||
<dd>An image URL to include in the preview. Ideally, a thumbnail image of size 120px by 120px, | ||
or a large image of size 280px by 150px.</dd> | ||
</dl> | ||
There is also an <code>extras</code> parameter to permit one to add extra parameters. Check | ||
<a href="https://dev.twitter.com/cards/markup" target="_blank">here</a> before adding extra parameters. | ||
</dd> | ||
<dt>Open Graph</dt> | ||
<dd>The <em>Open Graph</em> data are typically consumed by Facebook but can be read by other sites some even using | ||
it as a fall-back when their own protocol is unavailable. Details can be found at | ||
<a href="http://opengraphprotocol.org/">opengraphprotocol.org</a> and in Facebook documentation. | ||
<dl> | ||
<dt>Title</dt> | ||
<dd>The title of the content for the page.</dd> | ||
<dt>The content <code>type</code> of the page using the specific keyword.</dt> | ||
<dd>See <a href="http://opengraphprotocol.org/#types" target="_blank">here.</a> Each type can have its own | ||
tags associated with it. Some types include article, book, profile, and others.</dd> | ||
<dt>Image</dt> | ||
<dd>An image URL representing the graphed object. Ideally between 200px by 200px and 1200px by 630px</dd> | ||
<dt>URL</dt> | ||
<dd>The canonical URL of the graphed object. This is the distinct, authoritative, URL for the graphed object.</dd> | ||
<dt>Description</dt> | ||
<dd>A description of the object.</dd> | ||
</dl> | ||
</dd> | ||
<dt>Schema.org</dt> | ||
<dd>Schema.org is a robust and complicated meta data system. It is used by Google+ and others.</dd> | ||
<dd>In addition to typical markup, Schema.org needs to have the content inside a <em>scoped</em> tag. | ||
The expectation for this module is that this will be used on the <code>HTML</code> tag and the page level | ||
schema.org markup will be added to the <code>HEAD</code> with Meta tags via the <code>SchemaDotOrg</code> | ||
method. The simplest <em>type</em> to use for the <code>itemtype</code> is <code>Thing</code>. | ||
</dd> | ||
<dd><dl> | ||
<dt>Name</dt> | ||
<dd>Name of the item.</dd> | ||
<dt>Description</dt> | ||
<dd>Description of the item.</dd> | ||
<dt>Image</dt> | ||
<dd>An image associated with the item.</dd> | ||
</dl> | ||
This is also an <code>extras</code> parameter to permit one to add extra parameters. Check | ||
<a href="http://schema.org" target="_blank">schema.org</a> before adding extra parameters. | ||
</dd> | ||
</dl> | ||
<p>View source, then the head section of the document.</p> |
119 changes: 119 additions & 0 deletions
119
examples/src/main/scala/org/hyperscala/examples/ui/SocialMetadataExample.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
package org.hyperscala.examples.ui | ||
|
||
import org.hyperscala.bootstrap.component._ | ||
import org.hyperscala.html.tag | ||
import org.hyperscala.html.tag.HTML | ||
import org.hyperscala.javascript.dsl.window | ||
import org.hyperscala.selector.Selector | ||
import org.hyperscala.ui.module.SocialMetadata | ||
import org.hyperscala.web.Webpage | ||
import org.powerscala.{IO, Color} | ||
import org.hyperscala.BuildInfo | ||
import org.hyperscala.bootstrap.Bootstrap | ||
import org.hyperscala.css.SelectorStyleSheet | ||
import org.hyperscala.html._ | ||
import attributes.Target | ||
import org.hyperscala.css.attributes._ | ||
import language.reflectiveCalls | ||
import org.hyperscala.ui.module.SocialMetadata._ | ||
|
||
/** | ||
* Created by mmynsted on 4/17/15. | ||
* code@growingliberty.com | ||
* | ||
* Example for Social Media Meta data {{org.hyperscala.ui.module.SocialMetadata}} | ||
* | ||
*/ | ||
class SocialMetadataExample extends Webpage { | ||
this.require(SocialMetadata) | ||
this.require(Bootstrap) | ||
|
||
//Common Social Meta Data values | ||
val smTitle = "Hyperscala - SocialMetadata module" | ||
val smDescription = | ||
""" | ||
|Hyperscala module, SocialMetadata provides meta data in meta tags to improve social media | ||
|site linking and semantic search engine crawls. | ||
""".stripMargin | ||
val smKeyWords = "Hyperscala, module, social, media, data, twitter, facebook, open, graph" | ||
val smApplicationName = "Hyperscala" | ||
val smImage = "http://hyperscala.org/images/hyperscala.png" | ||
|
||
//Head | ||
title := smTitle | ||
head.contents += new tag.Link(rel = "stylesheet", href = "/css/style.css") | ||
head.contents += new tag.Link(href = "/css/social_meta_data.css") | ||
|
||
//main body content | ||
val main = new tag.Div { | ||
contents += new StaticHTML(IO.copy(getClass.getClassLoader.getResource("social_meta_data.html"))) | ||
} | ||
|
||
//Meta data | ||
head.contents += new tag.Comment("Social Meta Data: START") | ||
|
||
//base data | ||
head.contents += new tag.Comment("base data") | ||
SocialMetadata.BaseData(description = smDescription, keywords = Some(smKeyWords), | ||
applicationName = Some(smApplicationName)).tags foreach(head.contents += _) | ||
|
||
head.contents += new tag.Comment("twitter") | ||
SocialMetadata.TwitterData(title = smTitle, | ||
description = smDescription, image = Some(smImage)). tags foreach(head.contents += _) | ||
|
||
//Open Graph / Facebook | ||
head.contents += new tag.Comment("open graph/facebook") | ||
SocialMetadata.OpenGraphData(title = smTitle, contentType = "article", | ||
image = smImage, canonicalUrl = "http://hyperscala.org/example/ui/social_meta_data.html", | ||
description = smDescription, siteName = Some(smApplicationName)).tags foreach(head.contents += _) | ||
|
||
//Schema.org | ||
override lazy val html = new tag.HTML { this.makeScoped("Thing")} | ||
head.contents += new tag.Comment("schema.org") | ||
SocialMetadata.SchemaDotOrg(name = smTitle, description = smDescription, | ||
image = smImage).tags foreach(head.contents += _) | ||
|
||
head.contents += new tag.Comment("Social Meta Data: END") | ||
|
||
|
||
/* Cosmetic (Just make the content a little more readable) | ||
*/ | ||
body.role := "document" | ||
|
||
new SelectorStyleSheet(Selector.element[tag.Body])(body) { | ||
paddingTop := 100.px | ||
paddingBottom := 30.px | ||
} | ||
|
||
def sourceURL: String = null | ||
|
||
val container = new Container { | ||
clazz += "wrapper" | ||
|
||
if (sourceURL != null) { | ||
val filename = sourceURL.substring(sourceURL.lastIndexOf('/') + 1) | ||
contents += new Button(s"View $filename on GitHub", buttonStyle = ButtonStyle.Primary) { | ||
style.float := Float.Right | ||
clickEvent := window.open(sourceURL, Target.Blank) | ||
} | ||
} | ||
contents += main | ||
} | ||
body.contents += container | ||
body.contents += new tag.Footer { | ||
contents += new tag.I { | ||
style.display := Display.Block | ||
style.width := 1170.px | ||
style.marginLeft := Length.Auto | ||
style.marginRight := Length.Auto | ||
style.color := Color.White | ||
style.fontWeight := FontWeight.Bold | ||
style.paddingBottom := 30.px | ||
style.fontSize := FontSize.Small | ||
style.textAlign := Alignment.Right | ||
contents += s"©2015 Hyperscala.org, version: ${BuildInfo.version}, built: ${f"${BuildInfo.buildTime}%tc"}" | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
140 changes: 140 additions & 0 deletions
140
ui/src/main/scala/org/hyperscala/ui/module/SocialMetadata.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
package org.hyperscala.ui.module | ||
|
||
import org.hyperscala.Tag | ||
import org.hyperscala.html.tag.Meta | ||
import org.hyperscala.module.Module | ||
import org.hyperscala.web.{Website, Webpage} | ||
import org.powerscala.Version | ||
|
||
/** | ||
* Created by mmynsted on 4/17/15. | ||
* code@growingliberty.com | ||
* | ||
* Provide simple meta data to improve social media linking | ||
*/ | ||
object SocialMetadata extends Module { | ||
val name = "socialmetadata" | ||
val version = Version(1,0,0) | ||
|
||
override def init(website: Website): Unit = {} | ||
override def load(webpage: Webpage): Unit = {} | ||
|
||
trait SMData { | ||
def tags: Vector[Meta] | ||
def buildMetaData(j: (String, Option[String]) => Option[Meta])(data: (String, Option[String])*) = { | ||
data.foldLeft(Vector.empty[Meta])((b, a) => j(a._1, a._2) match { | ||
case Some(datum) => b :+ datum | ||
case _ => b | ||
}) | ||
} | ||
def maybeDatum(k: String, maybeV: Option[String]) = maybeV map (v => new Meta { | ||
content := v | ||
name := k | ||
}) | ||
def toOptionalValues(s: Seq[(String, String)]): Seq[(String, Option[String])] = s map{case (k,v) => (k, Option(v))} | ||
} | ||
|
||
/** | ||
* Basic Data applies to various social media site, search engines, etc. | ||
* | ||
* @param description of the page content | ||
* @param keywords to help identify the page and its content | ||
* @param author of the page when applicable | ||
* @param copyright when applicable | ||
* @param applicationName is the application-name for the site | ||
* @param extras is an optional [[Seq]] of extra parameters | ||
*/ | ||
case class BaseData(description: String, keywords: Option[String] = None, author: Option[String] = None, | ||
copyright: Option[String] = None, applicationName: Option[String] = None, | ||
extras: Seq[(String, String)] = Seq.empty[(String, String)]) extends SMData { | ||
def tags = { | ||
val items = Seq(("description", Some(description)), | ||
("keywords", keywords), ("author", author), ("copyright", copyright), | ||
("application-name", applicationName)) ++ toOptionalValues(extras) | ||
buildMetaData(maybeDatum)(items: _*) | ||
} | ||
} | ||
|
||
/** | ||
* Twitter specific meta data. | ||
* | ||
* @param title of page | ||
* @param description of page content | ||
* @param image that illustrates the meaning of the page | ||
* @param extras is an optional [[Seq]] of extra parameters | ||
*/ | ||
case class TwitterData(title: String, description: String, image: Option[String] = None, | ||
extras: Seq[(String, String)] = Seq.empty[(String, String)]) extends SMData { | ||
override def maybeDatum(k: String, maybeV: Option[String]) = super.maybeDatum(s"twitter:$k", maybeV) | ||
def tags = { | ||
val items = Seq(("card", Some("summary")), ("title", Some(title)), | ||
("description", Some(description)), ("image", image)) ++ toOptionalValues(extras) | ||
buildMetaData(maybeDatum)(items: _*) | ||
} | ||
} | ||
|
||
/** | ||
* Open Graph meta data. This is used mostly for Facebook though other sites/applications do read this. | ||
* | ||
* @param title of the page | ||
* @param contentType of the page, using the specific keyword. Look [[http://opengraphprotocol.org/#types here]]. "type" | ||
* @param image that illustrates the meaning of the page, specific to the type selected. | ||
* @param canonicalUrl is the single authoritative URL (in String format) for the given item of the given contentType. | ||
* @param description of the item and page | ||
* @param siteName where this page resides. "site_name" | ||
* @param extras is an optional [[Seq]] of extra parameters | ||
*/ | ||
case class OpenGraphData(title: String, contentType: String, image: String, canonicalUrl: String, | ||
description: String, siteName: Option[String] = None, | ||
extras: Seq[(String, String)] = Seq.empty[(String, String)]) extends SMData { | ||
override def maybeDatum(k: String, maybeV: Option[String]) = maybeV map (v => { | ||
val meta = new Meta { content := v } | ||
meta.attribute[String]("property", true) foreach(_ := s"og:$k") | ||
meta | ||
}) | ||
def tags = { | ||
val items = Seq(("title", Some(title)),("type", Some(contentType)), | ||
("image", Some(image)), ("url", Some(canonicalUrl)), ("description", Some(description)), | ||
("site_name", siteName)) ++ toOptionalValues(extras) | ||
buildMetaData(maybeDatum)(items: _*) | ||
} | ||
} | ||
|
||
/** | ||
* Schema.org - A robust and complicated meta data system. Used by Google+ and others. | ||
* Look at [[http://schema.org schema.org]] | ||
* | ||
* @param name of the item | ||
* @param description of the item | ||
* @param image can be URL or an ImageObject. (URL would be more compatible with other meta data) | ||
* @param extras optional [[Seq]] of extra parameters | ||
*/ | ||
case class SchemaDotOrg(name: String, description: String, image: String, | ||
extras: Seq[(String, String)] = Seq.empty[(String, String)]) extends SMData { | ||
override def maybeDatum(k: String, maybeV: Option[String]) = maybeV map (v => { | ||
val meta = new Meta { content := v } | ||
meta.attribute[String]("itemprop", true) foreach(_ := k) | ||
meta | ||
}) | ||
def tags = { | ||
val items = Seq(("name", Some(name)), ("description", Some(description)), | ||
("image", Some(image))) ++ toOptionalValues(extras) | ||
buildMetaData(maybeDatum)(items: _*) | ||
} | ||
} | ||
|
||
/** | ||
* Schema.org needs to have the content inside a ''scoped'' tag. To be compatible with the other meta data the | ||
* expectation is that this will be used on the `HTML` tag and the page level schema.org markup will be added to | ||
* the `HEAD` with Meta tags via [[SchemaDotOrg]] method. | ||
* | ||
* @param t is the tag, likely `HTML` to be decorated with the scope identifer and the itemtype. | ||
*/ | ||
implicit class ScopedTag(t: Tag) { | ||
def makeScoped(itemType: String): Unit = { | ||
t.attribute[Boolean]("itemscope", true) foreach(_ := true) | ||
t.attribute[String]("itemtype", true) foreach(_:= s"http://schema.org/$itemType") | ||
} | ||
} | ||
|
||
} |