This commit is contained in:
Andy Bunce 2012-09-23 17:21:48 +01:00
parent 84579b53c7
commit 49904dabed
8 changed files with 341 additions and 33 deletions

View File

@ -7,12 +7,16 @@
module namespace grxq = 'apb.graphviz.web';
declare default function namespace 'apb.graphviz.web';
import module namespace gr = 'apb.graphviz' at "lib/graphviz.xqm";
import module namespace dotui = 'apb.graphxq.dotui' at "dotui.xqm";
import module namespace txq = 'apb.txq' at "lib/txq.xqm";
import module namespace gr = 'apb.graphviz' at "lib/graphviz.xqm";
import module namespace oa="http://basex.org/ns/oauth" at "lib/oauth.xqy";
import module namespace config="apb.config" at "lib/config.xqm";
import module namespace request = "http://exquery.org/ns/request";
declare namespace rest = 'http://exquery.org/ns/restxq';
declare variable $grxq:layout:=fn:resolve-uri("views/layout.xml");
declare
@ -102,6 +106,14 @@ declare
return render("views/library.xml",$map)
};
declare
%rest:GET %rest:path("graphxq/twitter")
%output:method("html5")
function twitter( ) {
let $d:=$config:config
let $map:=map{"data":=fn:serialize($d) }
return render("views/twitter.xml",$map)
};
(:~ use dot or url :)
declare %private function getdot($dot,$url) as xs:string{
@ -131,7 +143,8 @@ declare function render($template,$locals){
"usermenu":=<div>users</div>,
"title":=request:path(),
"messages":=(),
"libserver":="//cdnjs.cloudflare.com/ajax/libs"}
"libserver":=$config:libserver,
"aceserver":=$config:aceserver}
let $locals:=map:new(($default,$locals))
return txq:render(fn:resolve-uri($template),$locals,$grxq:layout)
};

View File

@ -0,0 +1,33 @@
(:~
: configuration access
: looks in WEB-INF/site-config.xml for actual values
:
: @author andy bunce
: @since sept 2012
:)
module namespace config = 'apb.config';
declare default function namespace 'apb.config';
declare variable $config:default:=
<config>
<libserver>//cdnjs.cloudflare.com/ajax/libs</libserver>
<aceserver>http://d1n0x3qji82z53.cloudfront.net</aceserver>
<twitter>
<CONSUMER-KEY>CONSUMER-KEY</CONSUMER-KEY>
<CONSUMER-SECRET>CONSUMER-SECRET</CONSUMER-SECRET>
<access-token>your token</access-token>
<access-token-secret>your secret</access-token-secret>
</twitter>
</config>
;
declare variable $config:config:=
fn:doc(fn:resolve-uri("../../WEB-INF/site-config.xml"))/config
;
(:
: /twitter-bootstrap/2.1.1/..
: /jquery/1.8.1/jquery.min.js
:)
declare variable $config:libserver as xs:string:=$config:config/libserver/fn:string();
declare variable $config:aceserver as xs:string:=$config:config/aceserver/fn:string();

238
src/graphxq/lib/oauth.xqy Normal file
View File

@ -0,0 +1,238 @@
(:
: an OAuth implementation written in XQuery for BaseX 7.4
: based on http://norman.walsh.name/2010/09/25/oauth
:)
module namespace oa="http://basex.org/ns/oauth";
import module namespace crypto="http://expath.org/ns/crypto";
declare function oa:twitter-service($CONSUMER-KEY as xs:string,
$CONSUMER-SECRET as xs:string){
<oa:service-provider realm="">
<oa:request-token>
<oa:uri>http://twitter.com/oauth/request_token</oa:uri>
<oa:method>GET</oa:method>
</oa:request-token>
<oa:user-authorization>
<oa:uri>http://twitter.com/oauth/authorize</oa:uri>
</oa:user-authorization>
<oa:user-authentication>
<oa:uri>http://twitter.com/oauth/authenticate</oa:uri>
<oa:additional-params>force_login=true</oa:additional-params>
</oa:user-authentication>
<oa:access-token>
<oa:uri>http://twitter.com/oauth/access_token</oa:uri>
<oa:method>POST</oa:method>
</oa:access-token>
<oa:signature-methods>
<oa:method>HMAC-SHA1</oa:method>
</oa:signature-methods>
<oa:oauth-version>1.0</oa:oauth-version>
<oa:authentication>
<oa:consumer-key>{$CONSUMER-KEY}</oa:consumer-key>
<oa:consumer-key-secret>{$CONSUMER-SECRET}</oa:consumer-key-secret>
</oa:authentication>
</oa:service-provider>
};
declare function oa:timestamp() as xs:unsignedLong {
let $epoch := xs:dateTime('1970-01-01T00:00:00Z')
let $now := current-dateTime()
let $d := $now - $epoch
let $seconds
:= 86400 * days-from-duration($d)
+ 3600 * hours-from-duration($d)
+ 60 * minutes-from-duration($d)
+ seconds-from-duration($d)
return
xs:unsignedLong($seconds)
};
declare function oa:sign($key as xs:string, $data as xs:string) as xs:string {
crypto:hmac($data,$key,'SHA1','base64')
};
declare function oa:signature-method(
$service as element(oa:service-provider)
) as xs:string
{
if ($service/oa:signature-methods/oa:method = "HMAC-SHA1")
then "HMAC-SHA1"
else error(xs:QName("oa:BADSIGMETHOD"),
"Service must support 'HMAC-SHA1' signatures.")
};
declare function oa:http-method(
$proposed-method as xs:string
) as xs:string
{
if (upper-case($proposed-method) = "GET")
then "GET"
else if (upper-case($proposed-method) = "POST")
then "POST"
else error(xs:QName("oa:BADHTTPMETHOD"),
"Service must use HTTP GET or POST.")
};
declare function oa:request-token(
$service as element(oa:service-provider),
$callback as xs:string?)
as element(oa:request-token)
{
let $options := if (empty($callback))
then ()
else
<oa:options>
<oauth_callback>{$callback}</oauth_callback>
</oa:options>
let $data
:= oa:signed-request($service,
$service/oa:request-token/oa:method,
$service/oa:request-token/oa:uri,
$options, (), ())
return
<oa:request-token>
{ if ($data/oa:error)
then
$data/*
else
for $pair in tokenize($data, "&amp;")
return
element { concat("oa:", substring-before($pair, '=')) }
{ substring-after($pair, '=') }
}
</oa:request-token>
};
declare function oa:access-token(
$service as element(oa:service-provider),
$request as element(oa:request-token),
$verifier as xs:string)
as element(oa:access-token)
{
let $options := <oa:options><oauth_verifier>{$verifier}</oauth_verifier></oa:options>
let $data
:= oa:signed-request($service,
$service/oa:access-token/oa:method,
$service/oa:access-token/oa:uri,
$options,
$request/oa:oauth_token,
$request/oa:oaauth_token_secret)
return
<oa:access-token>
{ if ($data/oa:error)
then
$data/*
else
for $pair in tokenize($data, "&amp;")
return
element { concat("oa:", substring-before($pair, '=')) }
{ substring-after($pair, '=') }
}
</oa:access-token>
};
declare function oa:signed-request(
$service as element(oa:service-provider),
$method as xs:string,
$serviceuri as xs:string,
$options as element(oa:options)?,
$token as xs:string?,
$secret as xs:string?)
as element(oa:response)
{
let $realm := string($service/@realm)
let $nonce :=convert:integer-to-base(random:integer(),16)
let $stamp := oa:timestamp()
let $key := string($service/oa:authentication/oa:consumer-key)
let $sigkey := concat($service/oa:authentication/oa:consumer-key-secret,
"&amp;", if (empty($secret)) then "" else $secret)
let $version := string($service/oa:oauth-version)
let $sigmethod := oa:signature-method($service)
let $httpmethod := oa:http-method($method)
let $sigstruct
:= <oa:signature-base-string>
<oauth_consumer_key>{$key}</oauth_consumer_key>
<oauth_nonce>{$nonce}</oauth_nonce>
<oauth_signature_method>{$sigmethod}</oauth_signature_method>
<oauth_timestamp>{$stamp}</oauth_timestamp>
<oauth_version>{$version}</oauth_version>
{ if (not(empty($token)))
then <oauth_token>{$token}</oauth_token>
else ()
}
{ if (not(empty($options)))
then $options/*
else ()
}
</oa:signature-base-string>
let $encparams
:= for $field in $sigstruct/*
order by local-name($field)
return
concat(local-name($field), "=", encode-for-uri(string($field)))
let $sigbase := string-join(($httpmethod, encode-for-uri($serviceuri),
encode-for-uri(string-join($encparams,"&amp;"))), "&amp;")
let $signature := encode-for-uri(oa:sign($sigkey, $sigbase))
(: This is a bit of a pragmatic hack, what's the real answer? :)
let $authfields := $sigstruct/*[starts-with(local-name(.), "oauth_")
and not(self::oauth_callback)]
let $authheader := concat("OAuth realm=&quot;", $service/@realm, "&quot;, ",
"oauth_signature=&quot;", $signature, "&quot;, ",
string-join(
for $field in $authfields
return
concat(local-name($field),"=&quot;", encode-for-uri($field), "&quot;"),
", "))
let $uriparam := for $field in $options/*
return
concat(local-name($field),"=",encode-for-uri($field))
(: This strikes me as slightly weird. Twitter wants the parameters passed
encoded in the URI even for a POST. I don't know if that's a Twitter
quirk or the natural way that OAuth apps work. Anyway, if you find
this library isn't working for some other OAuth'd API, you might want
to play with this bit.
let $requri := if ($httpmethod = "GET")
then concat($serviceuri,
if (empty($uriparam)) then ''
else concat("?",string-join($uriparam,"&amp;")))
else $serviceuri
let $data := if ($httpmethod = "POST" and not(empty($uriparam)))
then <xh:data>{string-join($uriparam,"&amp;")}</xh:data>
else ()
:)
let $requri := concat($serviceuri,
if (empty($uriparam)) then ''
else concat("?",string-join($uriparam,"&amp;")))
let $request := <http:request method = "{$httpmethod}">
<http:header name="Authorization" value="{$authheader}"/>
</http:request>
let $tokenreq :=http:send-request($request,$requri)
return
<oa:response>
{ if (string($tokenreq[1]/@status) != "200")
then
(<oa:error>{$tokenreq[1]}</oa:error>,
<oa:error-body>{$tokenreq[2]}</oa:error-body>)
else
$tokenreq[2]
}
</oa:response>
};

View File

@ -48,10 +48,12 @@ jQuery(function($) { $.extend({
$(document).ready(function(){
$("#bnup").on("click",getsvg);
$("#bnsvg").on("click",function(){ $("#dotForm").submit()});
$("#bndn").on("click",function(){getsvg(true)});
$("#bnsvg").on("click",function(){ submit(false)});
$("#bndn").on("click",function(){submit(true)});
$("#dot").on("keyup",getsvg);
$('.colorpicker').colorPicker();
// set height where extend class
var resize=function(){
var h=$(window).height();
$('.extend').each(function(){
@ -64,6 +66,11 @@ $(document).ready(function(){
resize();
});
function submit(dl){
$("#ckdnload").prop("checked",dl);
$("#dotForm").submit()
}
function getsvg(dl){
var f=$("#dotForm").serializeArray()
var d=$("#frm-defaults").serializeArray()
@ -74,20 +81,21 @@ function getsvg(dl){
url:"svg",
data:f,
dataType: "text",
success: function(str){
// console.log(data)
var oParser = new DOMParser();
var data = oParser.parseFromString(str, "text/xml");
// http://stackoverflow.com/questions/3346106/accessing-a-dom-object-defined-in-an-external-svg-file
var n = document.importNode(data.documentElement,true);
$("#cuthere").empty().append(n);
$("#svgsrc").empty().text(str);
},
success: updateSvg,
error:function(jqXHR, textStatus, errorThrown){
console.log("ajax error: "+textStatus + errorThrown);
}
});
};
function updateSvg(str){
// console.log(data)
var oParser = new DOMParser();
var data = oParser.parseFromString(str, "text/xml");
// http://stackoverflow.com/questions/3346106/accessing-a-dom-object-defined-in-an-external-svg-file
var n = document.importNode(data.documentElement,true);
$("#cuthere").empty().append(n);
$("#svgsrc").empty().text(str);
};
function dotit(){
// xsvg.innerText="";

View File

@ -37,12 +37,12 @@
-->
<li class="follow-btn"><iframe title="Twitter Follow Button"
style="width: 237px; height: 20px;" class="twitter-follow-button"
src="http://platform.twitter.com/widgets/follow_button.html?id=twitter-widget-2&amp;lang=en&amp;screen_name=apb1704&amp;show_count=true&amp;show_screen_name=true&amp;size=m"
src="http://platform.twitter.com/widgets/follow_button.html?id=twitter-widget-2&amp;lang=en&amp;screen_name=apb1704&amp;show_count=false&amp;show_screen_name=true&amp;size=m"
allowtransparency="true" scrolling="no" frameborder="0"></iframe></li>
<li class="tweet-btn"><iframe title="Twitter Tweet Button"
style="width: 106px; height: 20px;"
class="twitter-share-button twitter-count-horizontal"
src="http://platform.twitter.com/widgets/tweet_button.html?count=horizontal&amp;id=twitter-widget-0&amp;lang=en&amp;original_referer=https%3A%2F%2Fgithub.com%2Fapb2006%2Fxqwebdoc%2F&amp;size=m&amp;text=xqwebdoc #xquery&amp;url=https%3A%2F%2Fgithub.com%2Fapb2006%2Fxqwebdoc&amp;via=apb1704"
src="http://platform.twitter.com/widgets/tweet_button.html?count=horizontal&amp;id=twitter-widget-0&amp;lang=en&amp;original_referer=https%3A%2F%2Fgithub.com%2Fapb2006%2Fgraphxq%2F&amp;size=m&amp;text=graphxq graphviz #xquery&amp;url=https%3A%2F%2Fgithub.com%2Fapb2006%2Fgraphxq&amp;via=apb1704"
allowtransparency="true" scrolling="no" frameborder="0"></iframe></li>
</ul>
</div>

View File

@ -4,7 +4,7 @@
<ul class="nav nav-tabs">
<li class="active"><a href="#tab-dot" data-toggle="tab">Dot</a></li>
<li><a href="#tab-defaults" data-toggle="tab">Defaults</a></li>
<li><a href="#tab-xml" data-toggle="tab">Raw XML</a></li>
<li><a href="#tab-xml" data-toggle="tab">SVG</a></li>
<li> <div class="btn-group">
<button id="bnup" class="btn btn-mini" ><i class="icon-refresh"></i>Redraw</button>
<button id="bnsvg" class="btn btn-mini" ><i class="icon-fullscreen"></i>SVG</button>
@ -13,19 +13,16 @@
</div>
</li>
</ul>
<div class="tab-content extend">
<div class="tab-pane active " id="tab-dot" >
<div class="tab-content" style="overflow:hidden">
<div class="tab-pane active " id="tab-dot" >
<form id="dotForm" action= "svg" method="post" target="_new" class="well " >
<label>digraph "
<input type="text" name="title" placeholder="title" class="input-small" />"
</label>
<span style="margin-right:-5px;padding-right:2px;">{{</span>
<textarea id="dot" name="dot" rows="100" style="width:90%;height:100%;overflow:scroll"
>{$dot}</textarea><span style="margin-right:-5px;">}}</span>
<label class="checkbox">
<input name="dl" type="checkbox"/> Download
</label>
<form id="dotForm" action= "svg" method="post" target="_new" >
<textarea id="dot" name="dot" rows="10" class="extend" style="width:90%;overflow:scroll"
>{$dot}</textarea>
<input id="ckdnload" name="dl" type="checkbox" style="display:none"/>
</form >
</div>
@ -63,13 +60,16 @@
</div>
<div class="span6">
<div id="svgdiv" class="extend" style="width:100%;height:30em;border: 1px solid #E3E3E3;min-height:20em;">{$svgwidget}</div>
<div id="svgdiv" class="extend well" style="width:95%;height:30em;min-height:20em;">{$svgwidget}</div>
</div>
<script src="http://d1n0x3qji82z53.cloudfront.net/src-min-noconflict/ace.js" type="text/javascript" charset="utf-8"></script>
<script src="{$aceserver}/src-min-noconflict/ace.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript">
$(document).ready(function(){{
getsvg(false)
$('#color1').change(function(){{
alert("color changed");
}});
}})
</script>
</div>

View File

@ -18,7 +18,7 @@
<link rel="shortcut icon" href="/graphxq/static/graphxq2.png" />
<script src="{$libserver}/jquery/1.8.1/jquery.min.js" type="text/javascript"></script>
<script src="{$libserver}/twitter-bootstrap/2.1.1/bootstrap.min.js" type="text/javascript"></script>
<script src="{$libserver}/twitter-bootstrap/2.1.1/js/bootstrap.min.js" type="text/javascript"></script>
<script src="/graphxq/static/jquery.colorPicker.js" type="text/javascript"></script>
<link href="/graphxq/static/colorPicker.css" rel="stylesheet" type="text/css" />
<script src="/graphxq/static/app.js" type="text/javascript"></script>
@ -68,8 +68,19 @@
<li class="nav">
<a href="library">Library</a>
</li>
<li class="nav">
<a href="about">About</a>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
About
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
<li>
<a href="about"><i class="icon-info-sign"></i> This application</a>
</li>
<li>
<a href="twitter"><i class="icon-comment"></i> Twitter test</a>
</li>
</ul>
</li>
</ul>
<form class="pull-right navbar-search" action="search">

View File

@ -0,0 +1,5 @@
<div>
<!-- q,collection, hits -->
<h1>No twitter yet</h1>
<pre>{$data}</pre>
</div>