Merge branch 'develop' into server2server_tls
This commit is contained in:
commit
f5755bcadf
|
@ -1026,7 +1026,7 @@ Getting/Setting your own presence state
|
||||||
REST Path: /presence/$user_id/status
|
REST Path: /presence/$user_id/status
|
||||||
Valid methods: GET/PUT
|
Valid methods: GET/PUT
|
||||||
Required keys:
|
Required keys:
|
||||||
state : [0|1|2|3] - The user's new presence state
|
presence : [0|1|2|3] - The user's new presence state
|
||||||
Optional keys:
|
Optional keys:
|
||||||
status_msg : text string provided by the user to explain their status
|
status_msg : text string provided by the user to explain their status
|
||||||
|
|
||||||
|
@ -1039,7 +1039,7 @@ Fetching your presence list
|
||||||
following keys:
|
following keys:
|
||||||
{
|
{
|
||||||
"user_id" : string giving the observed user's ID
|
"user_id" : string giving the observed user's ID
|
||||||
"state" : int giving their status
|
"presence" : int giving their status
|
||||||
"status_msg" : optional text string
|
"status_msg" : optional text string
|
||||||
"displayname" : optional text string from the user's profile
|
"displayname" : optional text string from the user's profile
|
||||||
"avatar_url" : optional text string from the user's profile
|
"avatar_url" : optional text string from the user's profile
|
||||||
|
|
|
@ -3,31 +3,31 @@
|
||||||
"swaggerVersion": "1.2",
|
"swaggerVersion": "1.2",
|
||||||
"apis": [
|
"apis": [
|
||||||
{
|
{
|
||||||
"path": "/login",
|
"path": "-login",
|
||||||
"description": "Login operations"
|
"description": "Login operations"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/registration",
|
"path": "-registration",
|
||||||
"description": "Registration operations"
|
"description": "Registration operations"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/rooms",
|
"path": "-rooms",
|
||||||
"description": "Room operations"
|
"description": "Room operations"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/profile",
|
"path": "-profile",
|
||||||
"description": "Profile operations"
|
"description": "Profile operations"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/presence",
|
"path": "-presence",
|
||||||
"description": "Presence operations"
|
"description": "Presence operations"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/events",
|
"path": "-events",
|
||||||
"description": "Event operations"
|
"description": "Event operations"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "/directory",
|
"path": "-directory",
|
||||||
"description": "Directory operations"
|
"description": "Directory operations"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
To get this running:
|
||||||
|
ln -s ../swagger_matrix
|
||||||
|
python -m SimpleHTTPServer
|
||||||
|
|
||||||
|
Go to http://localhost:8000/swagger.html
|
|
@ -0,0 +1,38 @@
|
||||||
|
// Backbone.js 0.9.2
|
||||||
|
|
||||||
|
// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
|
||||||
|
// Backbone may be freely distributed under the MIT license.
|
||||||
|
// For all details and documentation:
|
||||||
|
// http://backbonejs.org
|
||||||
|
(function(){var l=this,y=l.Backbone,z=Array.prototype.slice,A=Array.prototype.splice,g;g="undefined"!==typeof exports?exports:l.Backbone={};g.VERSION="0.9.2";var f=l._;!f&&"undefined"!==typeof require&&(f=require("underscore"));var i=l.jQuery||l.Zepto||l.ender;g.setDomLibrary=function(a){i=a};g.noConflict=function(){l.Backbone=y;return this};g.emulateHTTP=!1;g.emulateJSON=!1;var p=/\s+/,k=g.Events={on:function(a,b,c){var d,e,f,g,j;if(!b)return this;a=a.split(p);for(d=this._callbacks||(this._callbacks=
|
||||||
|
{});e=a.shift();)f=(j=d[e])?j.tail:{},f.next=g={},f.context=c,f.callback=b,d[e]={tail:g,next:j?j.next:f};return this},off:function(a,b,c){var d,e,h,g,j,q;if(e=this._callbacks){if(!a&&!b&&!c)return delete this._callbacks,this;for(a=a?a.split(p):f.keys(e);d=a.shift();)if(h=e[d],delete e[d],h&&(b||c))for(g=h.tail;(h=h.next)!==g;)if(j=h.callback,q=h.context,b&&j!==b||c&&q!==c)this.on(d,j,q);return this}},trigger:function(a){var b,c,d,e,f,g;if(!(d=this._callbacks))return this;f=d.all;a=a.split(p);for(g=
|
||||||
|
z.call(arguments,1);b=a.shift();){if(c=d[b])for(e=c.tail;(c=c.next)!==e;)c.callback.apply(c.context||this,g);if(c=f){e=c.tail;for(b=[b].concat(g);(c=c.next)!==e;)c.callback.apply(c.context||this,b)}}return this}};k.bind=k.on;k.unbind=k.off;var o=g.Model=function(a,b){var c;a||(a={});b&&b.parse&&(a=this.parse(a));if(c=n(this,"defaults"))a=f.extend({},c,a);b&&b.collection&&(this.collection=b.collection);this.attributes={};this._escapedAttributes={};this.cid=f.uniqueId("c");this.changed={};this._silent=
|
||||||
|
{};this._pending={};this.set(a,{silent:!0});this.changed={};this._silent={};this._pending={};this._previousAttributes=f.clone(this.attributes);this.initialize.apply(this,arguments)};f.extend(o.prototype,k,{changed:null,_silent:null,_pending:null,idAttribute:"id",initialize:function(){},toJSON:function(){return f.clone(this.attributes)},get:function(a){return this.attributes[a]},escape:function(a){var b;if(b=this._escapedAttributes[a])return b;b=this.get(a);return this._escapedAttributes[a]=f.escape(null==
|
||||||
|
b?"":""+b)},has:function(a){return null!=this.get(a)},set:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c||(c={});if(!d)return this;d instanceof o&&(d=d.attributes);if(c.unset)for(e in d)d[e]=void 0;if(!this._validate(d,c))return!1;this.idAttribute in d&&(this.id=d[this.idAttribute]);var b=c.changes={},h=this.attributes,g=this._escapedAttributes,j=this._previousAttributes||{};for(e in d){a=d[e];if(!f.isEqual(h[e],a)||c.unset&&f.has(h,e))delete g[e],(c.silent?this._silent:
|
||||||
|
b)[e]=!0;c.unset?delete h[e]:h[e]=a;!f.isEqual(j[e],a)||f.has(h,e)!=f.has(j,e)?(this.changed[e]=a,c.silent||(this._pending[e]=!0)):(delete this.changed[e],delete this._pending[e])}c.silent||this.change(c);return this},unset:function(a,b){(b||(b={})).unset=!0;return this.set(a,null,b)},clear:function(a){(a||(a={})).unset=!0;return this.set(f.clone(this.attributes),a)},fetch:function(a){var a=a?f.clone(a):{},b=this,c=a.success;a.success=function(d,e,f){if(!b.set(b.parse(d,f),a))return!1;c&&c(b,d)};
|
||||||
|
a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},save:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c=c?f.clone(c):{};if(c.wait){if(!this._validate(d,c))return!1;e=f.clone(this.attributes)}a=f.extend({},c,{silent:!0});if(d&&!this.set(d,c.wait?a:c))return!1;var h=this,i=c.success;c.success=function(a,b,e){b=h.parse(a,e);if(c.wait){delete c.wait;b=f.extend(d||{},b)}if(!h.set(b,c))return false;i?i(h,a):h.trigger("sync",h,a,c)};c.error=g.wrapError(c.error,
|
||||||
|
h,c);b=this.isNew()?"create":"update";b=(this.sync||g.sync).call(this,b,this,c);c.wait&&this.set(e,a);return b},destroy:function(a){var a=a?f.clone(a):{},b=this,c=a.success,d=function(){b.trigger("destroy",b,b.collection,a)};if(this.isNew())return d(),!1;a.success=function(e){a.wait&&d();c?c(b,e):b.trigger("sync",b,e,a)};a.error=g.wrapError(a.error,b,a);var e=(this.sync||g.sync).call(this,"delete",this,a);a.wait||d();return e},url:function(){var a=n(this,"urlRoot")||n(this.collection,"url")||t();
|
||||||
|
return this.isNew()?a:a+("/"==a.charAt(a.length-1)?"":"/")+encodeURIComponent(this.id)},parse:function(a){return a},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return null==this.id},change:function(a){a||(a={});var b=this._changing;this._changing=!0;for(var c in this._silent)this._pending[c]=!0;var d=f.extend({},a.changes,this._silent);this._silent={};for(c in d)this.trigger("change:"+c,this,this.get(c),a);if(b)return this;for(;!f.isEmpty(this._pending);){this._pending=
|
||||||
|
{};this.trigger("change",this,a);for(c in this.changed)!this._pending[c]&&!this._silent[c]&&delete this.changed[c];this._previousAttributes=f.clone(this.attributes)}this._changing=!1;return this},hasChanged:function(a){return!arguments.length?!f.isEmpty(this.changed):f.has(this.changed,a)},changedAttributes:function(a){if(!a)return this.hasChanged()?f.clone(this.changed):!1;var b,c=!1,d=this._previousAttributes,e;for(e in a)if(!f.isEqual(d[e],b=a[e]))(c||(c={}))[e]=b;return c},previous:function(a){return!arguments.length||
|
||||||
|
!this._previousAttributes?null:this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},isValid:function(){return!this.validate(this.attributes)},_validate:function(a,b){if(b.silent||!this.validate)return!0;var a=f.extend({},this.attributes,a),c=this.validate(a,b);if(!c)return!0;b&&b.error?b.error(this,c,b):this.trigger("error",this,c,b);return!1}});var r=g.Collection=function(a,b){b||(b={});b.model&&(this.model=b.model);b.comparator&&(this.comparator=b.comparator);
|
||||||
|
this._reset();this.initialize.apply(this,arguments);a&&this.reset(a,{silent:!0,parse:b.parse})};f.extend(r.prototype,k,{model:o,initialize:function(){},toJSON:function(a){return this.map(function(b){return b.toJSON(a)})},add:function(a,b){var c,d,e,g,i,j={},k={},l=[];b||(b={});a=f.isArray(a)?a.slice():[a];c=0;for(d=a.length;c<d;c++){if(!(e=a[c]=this._prepareModel(a[c],b)))throw Error("Can't add an invalid model to a collection");g=e.cid;i=e.id;j[g]||this._byCid[g]||null!=i&&(k[i]||this._byId[i])?
|
||||||
|
l.push(c):j[g]=k[i]=e}for(c=l.length;c--;)a.splice(l[c],1);c=0;for(d=a.length;c<d;c++)(e=a[c]).on("all",this._onModelEvent,this),this._byCid[e.cid]=e,null!=e.id&&(this._byId[e.id]=e);this.length+=d;A.apply(this.models,[null!=b.at?b.at:this.models.length,0].concat(a));this.comparator&&this.sort({silent:!0});if(b.silent)return this;c=0;for(d=this.models.length;c<d;c++)if(j[(e=this.models[c]).cid])b.index=c,e.trigger("add",e,this,b);return this},remove:function(a,b){var c,d,e,g;b||(b={});a=f.isArray(a)?
|
||||||
|
a.slice():[a];c=0;for(d=a.length;c<d;c++)if(g=this.getByCid(a[c])||this.get(a[c]))delete this._byId[g.id],delete this._byCid[g.cid],e=this.indexOf(g),this.models.splice(e,1),this.length--,b.silent||(b.index=e,g.trigger("remove",g,this,b)),this._removeReference(g);return this},push:function(a,b){a=this._prepareModel(a,b);this.add(a,b);return a},pop:function(a){var b=this.at(this.length-1);this.remove(b,a);return b},unshift:function(a,b){a=this._prepareModel(a,b);this.add(a,f.extend({at:0},b));return a},
|
||||||
|
shift:function(a){var b=this.at(0);this.remove(b,a);return b},get:function(a){return null==a?void 0:this._byId[null!=a.id?a.id:a]},getByCid:function(a){return a&&this._byCid[a.cid||a]},at:function(a){return this.models[a]},where:function(a){return f.isEmpty(a)?[]:this.filter(function(b){for(var c in a)if(a[c]!==b.get(c))return!1;return!0})},sort:function(a){a||(a={});if(!this.comparator)throw Error("Cannot sort a set without a comparator");var b=f.bind(this.comparator,this);1==this.comparator.length?
|
||||||
|
this.models=this.sortBy(b):this.models.sort(b);a.silent||this.trigger("reset",this,a);return this},pluck:function(a){return f.map(this.models,function(b){return b.get(a)})},reset:function(a,b){a||(a=[]);b||(b={});for(var c=0,d=this.models.length;c<d;c++)this._removeReference(this.models[c]);this._reset();this.add(a,f.extend({silent:!0},b));b.silent||this.trigger("reset",this,b);return this},fetch:function(a){a=a?f.clone(a):{};void 0===a.parse&&(a.parse=!0);var b=this,c=a.success;a.success=function(d,
|
||||||
|
e,f){b[a.add?"add":"reset"](b.parse(d,f),a);c&&c(b,d)};a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},create:function(a,b){var c=this,b=b?f.clone(b):{},a=this._prepareModel(a,b);if(!a)return!1;b.wait||c.add(a,b);var d=b.success;b.success=function(e,f){b.wait&&c.add(e,b);d?d(e,f):e.trigger("sync",a,f,b)};a.save(null,b);return a},parse:function(a){return a},chain:function(){return f(this.models).chain()},_reset:function(){this.length=0;this.models=[];this._byId=
|
||||||
|
{};this._byCid={}},_prepareModel:function(a,b){b||(b={});a instanceof o?a.collection||(a.collection=this):(b.collection=this,a=new this.model(a,b),a._validate(a.attributes,b)||(a=!1));return a},_removeReference:function(a){this==a.collection&&delete a.collection;a.off("all",this._onModelEvent,this)},_onModelEvent:function(a,b,c,d){("add"==a||"remove"==a)&&c!=this||("destroy"==a&&this.remove(b,d),b&&a==="change:"+b.idAttribute&&(delete this._byId[b.previous(b.idAttribute)],this._byId[b.id]=b),this.trigger.apply(this,
|
||||||
|
arguments))}});f.each("forEach,each,map,reduce,reduceRight,find,detect,filter,select,reject,every,all,some,any,include,contains,invoke,max,min,sortBy,sortedIndex,toArray,size,first,initial,rest,last,without,indexOf,shuffle,lastIndexOf,isEmpty,groupBy".split(","),function(a){r.prototype[a]=function(){return f[a].apply(f,[this.models].concat(f.toArray(arguments)))}});var u=g.Router=function(a){a||(a={});a.routes&&(this.routes=a.routes);this._bindRoutes();this.initialize.apply(this,arguments)},B=/:\w+/g,
|
||||||
|
C=/\*\w+/g,D=/[-[\]{}()+?.,\\^$|#\s]/g;f.extend(u.prototype,k,{initialize:function(){},route:function(a,b,c){g.history||(g.history=new m);f.isRegExp(a)||(a=this._routeToRegExp(a));c||(c=this[b]);g.history.route(a,f.bind(function(d){d=this._extractParameters(a,d);c&&c.apply(this,d);this.trigger.apply(this,["route:"+b].concat(d));g.history.trigger("route",this,b,d)},this));return this},navigate:function(a,b){g.history.navigate(a,b)},_bindRoutes:function(){if(this.routes){var a=[],b;for(b in this.routes)a.unshift([b,
|
||||||
|
this.routes[b]]);b=0;for(var c=a.length;b<c;b++)this.route(a[b][0],a[b][1],this[a[b][1]])}},_routeToRegExp:function(a){a=a.replace(D,"\\$&").replace(B,"([^/]+)").replace(C,"(.*?)");return RegExp("^"+a+"$")},_extractParameters:function(a,b){return a.exec(b).slice(1)}});var m=g.History=function(){this.handlers=[];f.bindAll(this,"checkUrl")},s=/^[#\/]/,E=/msie [\w.]+/;m.started=!1;f.extend(m.prototype,k,{interval:50,getHash:function(a){return(a=(a?a.location:window.location).href.match(/#(.*)$/))?a[1]:
|
||||||
|
""},getFragment:function(a,b){if(null==a)if(this._hasPushState||b){var a=window.location.pathname,c=window.location.search;c&&(a+=c)}else a=this.getHash();a.indexOf(this.options.root)||(a=a.substr(this.options.root.length));return a.replace(s,"")},start:function(a){if(m.started)throw Error("Backbone.history has already been started");m.started=!0;this.options=f.extend({},{root:"/"},this.options,a);this._wantsHashChange=!1!==this.options.hashChange;this._wantsPushState=!!this.options.pushState;this._hasPushState=
|
||||||
|
!(!this.options.pushState||!window.history||!window.history.pushState);var a=this.getFragment(),b=document.documentMode;if(b=E.exec(navigator.userAgent.toLowerCase())&&(!b||7>=b))this.iframe=i('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo("body")[0].contentWindow,this.navigate(a);this._hasPushState?i(window).bind("popstate",this.checkUrl):this._wantsHashChange&&"onhashchange"in window&&!b?i(window).bind("hashchange",this.checkUrl):this._wantsHashChange&&(this._checkUrlInterval=setInterval(this.checkUrl,
|
||||||
|
this.interval));this.fragment=a;a=window.location;b=a.pathname==this.options.root;if(this._wantsHashChange&&this._wantsPushState&&!this._hasPushState&&!b)return this.fragment=this.getFragment(null,!0),window.location.replace(this.options.root+"#"+this.fragment),!0;this._wantsPushState&&this._hasPushState&&b&&a.hash&&(this.fragment=this.getHash().replace(s,""),window.history.replaceState({},document.title,a.protocol+"//"+a.host+this.options.root+this.fragment));if(!this.options.silent)return this.loadUrl()},
|
||||||
|
stop:function(){i(window).unbind("popstate",this.checkUrl).unbind("hashchange",this.checkUrl);clearInterval(this._checkUrlInterval);m.started=!1},route:function(a,b){this.handlers.unshift({route:a,callback:b})},checkUrl:function(){var a=this.getFragment();a==this.fragment&&this.iframe&&(a=this.getFragment(this.getHash(this.iframe)));if(a==this.fragment)return!1;this.iframe&&this.navigate(a);this.loadUrl()||this.loadUrl(this.getHash())},loadUrl:function(a){var b=this.fragment=this.getFragment(a);return f.any(this.handlers,
|
||||||
|
function(a){if(a.route.test(b))return a.callback(b),!0})},navigate:function(a,b){if(!m.started)return!1;if(!b||!0===b)b={trigger:b};var c=(a||"").replace(s,"");this.fragment!=c&&(this._hasPushState?(0!=c.indexOf(this.options.root)&&(c=this.options.root+c),this.fragment=c,window.history[b.replace?"replaceState":"pushState"]({},document.title,c)):this._wantsHashChange?(this.fragment=c,this._updateHash(window.location,c,b.replace),this.iframe&&c!=this.getFragment(this.getHash(this.iframe))&&(b.replace||
|
||||||
|
this.iframe.document.open().close(),this._updateHash(this.iframe.location,c,b.replace))):window.location.assign(this.options.root+a),b.trigger&&this.loadUrl(a))},_updateHash:function(a,b,c){c?a.replace(a.toString().replace(/(javascript:|#).*$/,"")+"#"+b):a.hash=b}});var v=g.View=function(a){this.cid=f.uniqueId("view");this._configure(a||{});this._ensureElement();this.initialize.apply(this,arguments);this.delegateEvents()},F=/^(\S+)\s*(.*)$/,w="model,collection,el,id,attributes,className,tagName".split(",");
|
||||||
|
f.extend(v.prototype,k,{tagName:"div",$:function(a){return this.$el.find(a)},initialize:function(){},render:function(){return this},remove:function(){this.$el.remove();return this},make:function(a,b,c){a=document.createElement(a);b&&i(a).attr(b);c&&i(a).html(c);return a},setElement:function(a,b){this.$el&&this.undelegateEvents();this.$el=a instanceof i?a:i(a);this.el=this.$el[0];!1!==b&&this.delegateEvents();return this},delegateEvents:function(a){if(a||(a=n(this,"events"))){this.undelegateEvents();
|
||||||
|
for(var b in a){var c=a[b];f.isFunction(c)||(c=this[a[b]]);if(!c)throw Error('Method "'+a[b]+'" does not exist');var d=b.match(F),e=d[1],d=d[2],c=f.bind(c,this),e=e+(".delegateEvents"+this.cid);""===d?this.$el.bind(e,c):this.$el.delegate(d,e,c)}}},undelegateEvents:function(){this.$el.unbind(".delegateEvents"+this.cid)},_configure:function(a){this.options&&(a=f.extend({},this.options,a));for(var b=0,c=w.length;b<c;b++){var d=w[b];a[d]&&(this[d]=a[d])}this.options=a},_ensureElement:function(){if(this.el)this.setElement(this.el,
|
||||||
|
!1);else{var a=n(this,"attributes")||{};this.id&&(a.id=this.id);this.className&&(a["class"]=this.className);this.setElement(this.make(this.tagName,a),!1)}}});o.extend=r.extend=u.extend=v.extend=function(a,b){var c=G(this,a,b);c.extend=this.extend;return c};var H={create:"POST",update:"PUT","delete":"DELETE",read:"GET"};g.sync=function(a,b,c){var d=H[a];c||(c={});var e={type:d,dataType:"json"};c.url||(e.url=n(b,"url")||t());if(!c.data&&b&&("create"==a||"update"==a))e.contentType="application/json",
|
||||||
|
e.data=JSON.stringify(b.toJSON());g.emulateJSON&&(e.contentType="application/x-www-form-urlencoded",e.data=e.data?{model:e.data}:{});if(g.emulateHTTP&&("PUT"===d||"DELETE"===d))g.emulateJSON&&(e.data._method=d),e.type="POST",e.beforeSend=function(a){a.setRequestHeader("X-HTTP-Method-Override",d)};"GET"!==e.type&&!g.emulateJSON&&(e.processData=!1);return i.ajax(f.extend(e,c))};g.wrapError=function(a,b,c){return function(d,e){e=d===b?e:d;a?a(b,e,c):b.trigger("error",b,e,c)}};var x=function(){},G=function(a,
|
||||||
|
b,c){var d;d=b&&b.hasOwnProperty("constructor")?b.constructor:function(){a.apply(this,arguments)};f.extend(d,a);x.prototype=a.prototype;d.prototype=new x;b&&f.extend(d.prototype,b);c&&f.extend(d,c);d.prototype.constructor=d;d.__super__=a.prototype;return d},n=function(a,b){return!a||!a[b]?null:f.isFunction(a[b])?a[b]():a[b]},t=function(){throw Error('A "url" property or function must be specified');}}).call(this);
|
|
@ -0,0 +1,16 @@
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Droid Sans';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: local('Droid Sans'), local('DroidSans'), url(http://fonts.gstatic.com/s/droidsans/v5/s-BiyweUPV0v-yRb-cjciPk_vArhqVIZ0nv9q090hN8.woff2) format('woff2');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
|
||||||
|
}
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Droid Sans';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
src: local('Droid Sans Bold'), local('DroidSans-Bold'), url(http://fonts.gstatic.com/s/droidsans/v5/EFpQQyG9GqCrobXxL-KRMYWiMMZ7xLd792ULpGE4W_Y.woff2) format('woff2');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,18 @@
|
||||||
|
/*
|
||||||
|
* jQuery BBQ: Back Button & Query Library - v1.2.1 - 2/17/2010
|
||||||
|
* http://benalman.com/projects/jquery-bbq-plugin/
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010 "Cowboy" Ben Alman
|
||||||
|
* Dual licensed under the MIT and GPL licenses.
|
||||||
|
* http://benalman.com/about/license/
|
||||||
|
*/
|
||||||
|
(function($,p){var i,m=Array.prototype.slice,r=decodeURIComponent,a=$.param,c,l,v,b=$.bbq=$.bbq||{},q,u,j,e=$.event.special,d="hashchange",A="querystring",D="fragment",y="elemUrlAttr",g="location",k="href",t="src",x=/^.*\?|#.*$/g,w=/^.*\#/,h,C={};function E(F){return typeof F==="string"}function B(G){var F=m.call(arguments,1);return function(){return G.apply(this,F.concat(m.call(arguments)))}}function n(F){return F.replace(/^[^#]*#?(.*)$/,"$1")}function o(F){return F.replace(/(?:^[^?#]*\?([^#]*).*$)?.*/,"$1")}function f(H,M,F,I,G){var O,L,K,N,J;if(I!==i){K=F.match(H?/^([^#]*)\#?(.*)$/:/^([^#?]*)\??([^#]*)(#?.*)/);J=K[3]||"";if(G===2&&E(I)){L=I.replace(H?w:x,"")}else{N=l(K[2]);I=E(I)?l[H?D:A](I):I;L=G===2?I:G===1?$.extend({},I,N):$.extend({},N,I);L=a(L);if(H){L=L.replace(h,r)}}O=K[1]+(H?"#":L||!K[1]?"?":"")+L+J}else{O=M(F!==i?F:p[g][k])}return O}a[A]=B(f,0,o);a[D]=c=B(f,1,n);c.noEscape=function(G){G=G||"";var F=$.map(G.split(""),encodeURIComponent);h=new RegExp(F.join("|"),"g")};c.noEscape(",/");$.deparam=l=function(I,F){var H={},G={"true":!0,"false":!1,"null":null};$.each(I.replace(/\+/g," ").split("&"),function(L,Q){var K=Q.split("="),P=r(K[0]),J,O=H,M=0,R=P.split("]["),N=R.length-1;if(/\[/.test(R[0])&&/\]$/.test(R[N])){R[N]=R[N].replace(/\]$/,"");R=R.shift().split("[").concat(R);N=R.length-1}else{N=0}if(K.length===2){J=r(K[1]);if(F){J=J&&!isNaN(J)?+J:J==="undefined"?i:G[J]!==i?G[J]:J}if(N){for(;M<=N;M++){P=R[M]===""?O.length:R[M];O=O[P]=M<N?O[P]||(R[M+1]&&isNaN(R[M+1])?{}:[]):J}}else{if($.isArray(H[P])){H[P].push(J)}else{if(H[P]!==i){H[P]=[H[P],J]}else{H[P]=J}}}}else{if(P){H[P]=F?i:""}}});return H};function z(H,F,G){if(F===i||typeof F==="boolean"){G=F;F=a[H?D:A]()}else{F=E(F)?F.replace(H?w:x,""):F}return l(F,G)}l[A]=B(z,0);l[D]=v=B(z,1);$[y]||($[y]=function(F){return $.extend(C,F)})({a:k,base:k,iframe:t,img:t,input:t,form:"action",link:k,script:t});j=$[y];function s(I,G,H,F){if(!E(H)&&typeof H!=="object"){F=H;H=G;G=i}return this.each(function(){var L=$(this),J=G||j()[(this.nodeName||"").toLowerCase()]||"",K=J&&L.attr(J)||"";L.attr(J,a[I](K,H,F))})}$.fn[A]=B(s,A);$.fn[D]=B(s,D);b.pushState=q=function(I,F){if(E(I)&&/^#/.test(I)&&F===i){F=2}var H=I!==i,G=c(p[g][k],H?I:{},H?F:2);p[g][k]=G+(/#/.test(G)?"":"#")};b.getState=u=function(F,G){return F===i||typeof F==="boolean"?v(F):v(G)[F]};b.removeState=function(F){var G={};if(F!==i){G=u();$.each($.isArray(F)?F:arguments,function(I,H){delete G[H]})}q(G,2)};e[d]=$.extend(e[d],{add:function(F){var H;function G(J){var I=J[D]=c();J.getState=function(K,L){return K===i||typeof K==="boolean"?l(I,K):l(I,L)[K]};H.apply(this,arguments)}if($.isFunction(F)){H=F;return G}else{H=F.handler;F.handler=G}}})})(jQuery,this);
|
||||||
|
/*
|
||||||
|
* jQuery hashchange event - v1.2 - 2/11/2010
|
||||||
|
* http://benalman.com/projects/jquery-hashchange-plugin/
|
||||||
|
*
|
||||||
|
* Copyright (c) 2010 "Cowboy" Ben Alman
|
||||||
|
* Dual licensed under the MIT and GPL licenses.
|
||||||
|
* http://benalman.com/about/license/
|
||||||
|
*/
|
||||||
|
(function($,i,b){var j,k=$.event.special,c="location",d="hashchange",l="href",f=$.browser,g=document.documentMode,h=f.msie&&(g===b||g<8),e="on"+d in i&&!h;function a(m){m=m||i[c][l];return m.replace(/^[^#]*#?(.*)$/,"$1")}$[d+"Delay"]=100;k[d]=$.extend(k[d],{setup:function(){if(e){return false}$(j.start)},teardown:function(){if(e){return false}$(j.stop)}});j=(function(){var m={},r,n,o,q;function p(){o=q=function(s){return s};if(h){n=$('<iframe src="javascript:0"/>').hide().insertAfter("body")[0].contentWindow;q=function(){return a(n.document[c][l])};o=function(u,s){if(u!==s){var t=n.document;t.open().close();t[c].hash="#"+u}};o(a())}}m.start=function(){if(r){return}var t=a();o||p();(function s(){var v=a(),u=q(t);if(v!==t){o(t=v,u);$(i).trigger(d)}else{if(u!==t){i[c][l]=i[c][l].replace(/#.*/,"")+"#"+u}}r=setTimeout(s,$[d+"Delay"])})()};m.stop=function(){if(!n){r&&clearTimeout(r);r=0}};return m})()})(jQuery,this);
|
|
@ -0,0 +1 @@
|
||||||
|
(function(b){b.fn.slideto=function(a){a=b.extend({slide_duration:"slow",highlight_duration:3E3,highlight:true,highlight_color:"#FFFF99"},a);return this.each(function(){obj=b(this);b("body").animate({scrollTop:obj.offset().top},a.slide_duration,function(){a.highlight&&b.ui.version&&obj.effect("highlight",{color:a.highlight_color},a.highlight_duration)})})}})(jQuery);
|
|
@ -0,0 +1,8 @@
|
||||||
|
/*
|
||||||
|
jQuery Wiggle
|
||||||
|
Author: WonderGroup, Jordan Thomas
|
||||||
|
URL: http://labs.wondergroup.com/demos/mini-ui/index.html
|
||||||
|
License: MIT (http://en.wikipedia.org/wiki/MIT_License)
|
||||||
|
*/
|
||||||
|
jQuery.fn.wiggle=function(o){var d={speed:50,wiggles:3,travel:5,callback:null};var o=jQuery.extend(d,o);return this.each(function(){var cache=this;var wrap=jQuery(this).wrap('<div class="wiggle-wrap"></div>').css("position","relative");var calls=0;for(i=1;i<=o.wiggles;i++){jQuery(this).animate({left:"-="+o.travel},o.speed).animate({left:"+="+o.travel*2},o.speed*2).animate({left:"-="+o.travel},o.speed,function(){calls++;if(jQuery(cache).parent().hasClass('wiggle-wrap')){jQuery(cache).parent().replaceWith(cache);}
|
||||||
|
if(calls==o.wiggles&&jQuery.isFunction(o.callback)){o.callback();}});}});};
|
|
@ -0,0 +1,125 @@
|
||||||
|
/* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 */
|
||||||
|
html,
|
||||||
|
body,
|
||||||
|
div,
|
||||||
|
span,
|
||||||
|
applet,
|
||||||
|
object,
|
||||||
|
iframe,
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6,
|
||||||
|
p,
|
||||||
|
blockquote,
|
||||||
|
pre,
|
||||||
|
a,
|
||||||
|
abbr,
|
||||||
|
acronym,
|
||||||
|
address,
|
||||||
|
big,
|
||||||
|
cite,
|
||||||
|
code,
|
||||||
|
del,
|
||||||
|
dfn,
|
||||||
|
em,
|
||||||
|
img,
|
||||||
|
ins,
|
||||||
|
kbd,
|
||||||
|
q,
|
||||||
|
s,
|
||||||
|
samp,
|
||||||
|
small,
|
||||||
|
strike,
|
||||||
|
strong,
|
||||||
|
sub,
|
||||||
|
sup,
|
||||||
|
tt,
|
||||||
|
var,
|
||||||
|
b,
|
||||||
|
u,
|
||||||
|
i,
|
||||||
|
center,
|
||||||
|
dl,
|
||||||
|
dt,
|
||||||
|
dd,
|
||||||
|
ol,
|
||||||
|
ul,
|
||||||
|
li,
|
||||||
|
fieldset,
|
||||||
|
form,
|
||||||
|
label,
|
||||||
|
legend,
|
||||||
|
table,
|
||||||
|
caption,
|
||||||
|
tbody,
|
||||||
|
tfoot,
|
||||||
|
thead,
|
||||||
|
tr,
|
||||||
|
th,
|
||||||
|
td,
|
||||||
|
article,
|
||||||
|
aside,
|
||||||
|
canvas,
|
||||||
|
details,
|
||||||
|
embed,
|
||||||
|
figure,
|
||||||
|
figcaption,
|
||||||
|
footer,
|
||||||
|
header,
|
||||||
|
hgroup,
|
||||||
|
menu,
|
||||||
|
nav,
|
||||||
|
output,
|
||||||
|
ruby,
|
||||||
|
section,
|
||||||
|
summary,
|
||||||
|
time,
|
||||||
|
mark,
|
||||||
|
audio,
|
||||||
|
video {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
font-size: 100%;
|
||||||
|
font: inherit;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
/* HTML5 display-role reset for older browsers */
|
||||||
|
article,
|
||||||
|
aside,
|
||||||
|
details,
|
||||||
|
figcaption,
|
||||||
|
figure,
|
||||||
|
footer,
|
||||||
|
header,
|
||||||
|
hgroup,
|
||||||
|
menu,
|
||||||
|
nav,
|
||||||
|
section {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
ol,
|
||||||
|
ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
blockquote,
|
||||||
|
q {
|
||||||
|
quotes: none;
|
||||||
|
}
|
||||||
|
blockquote:before,
|
||||||
|
blockquote:after,
|
||||||
|
q:before,
|
||||||
|
q:after {
|
||||||
|
content: '';
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,211 @@
|
||||||
|
var appName;
|
||||||
|
var popupMask;
|
||||||
|
var popupDialog;
|
||||||
|
var clientId;
|
||||||
|
var realm;
|
||||||
|
|
||||||
|
function handleLogin() {
|
||||||
|
var scopes = [];
|
||||||
|
|
||||||
|
if(window.swaggerUi.api.authSchemes
|
||||||
|
&& window.swaggerUi.api.authSchemes.oauth2
|
||||||
|
&& window.swaggerUi.api.authSchemes.oauth2.scopes) {
|
||||||
|
scopes = window.swaggerUi.api.authSchemes.oauth2.scopes;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(window.swaggerUi.api
|
||||||
|
&& window.swaggerUi.api.info) {
|
||||||
|
appName = window.swaggerUi.api.info.title;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(popupDialog.length > 0)
|
||||||
|
popupDialog = popupDialog.last();
|
||||||
|
else {
|
||||||
|
popupDialog = $(
|
||||||
|
[
|
||||||
|
'<div class="api-popup-dialog">',
|
||||||
|
'<div class="api-popup-title">Select OAuth2.0 Scopes</div>',
|
||||||
|
'<div class="api-popup-content">',
|
||||||
|
'<p>Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes.',
|
||||||
|
'<a href="#">Learn how to use</a>',
|
||||||
|
'</p>',
|
||||||
|
'<p><strong>' + appName + '</strong> API requires the following scopes. Select which ones you want to grant to Swagger UI.</p>',
|
||||||
|
'<ul class="api-popup-scopes">',
|
||||||
|
'</ul>',
|
||||||
|
'<p class="error-msg"></p>',
|
||||||
|
'<div class="api-popup-actions"><button class="api-popup-authbtn api-button green" type="button">Authorize</button><button class="api-popup-cancel api-button gray" type="button">Cancel</button></div>',
|
||||||
|
'</div>',
|
||||||
|
'</div>'].join(''));
|
||||||
|
$(document.body).append(popupDialog);
|
||||||
|
|
||||||
|
popup = popupDialog.find('ul.api-popup-scopes').empty();
|
||||||
|
for (i = 0; i < scopes.length; i ++) {
|
||||||
|
scope = scopes[i];
|
||||||
|
str = '<li><input type="checkbox" id="scope_' + i + '" scope="' + scope.scope + '"/>' + '<label for="scope_' + i + '">' + scope.scope;
|
||||||
|
if (scope.description) {
|
||||||
|
str += '<br/><span class="api-scope-desc">' + scope.description + '</span>';
|
||||||
|
}
|
||||||
|
str += '</label></li>';
|
||||||
|
popup.append(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var $win = $(window),
|
||||||
|
dw = $win.width(),
|
||||||
|
dh = $win.height(),
|
||||||
|
st = $win.scrollTop(),
|
||||||
|
dlgWd = popupDialog.outerWidth(),
|
||||||
|
dlgHt = popupDialog.outerHeight(),
|
||||||
|
top = (dh -dlgHt)/2 + st,
|
||||||
|
left = (dw - dlgWd)/2;
|
||||||
|
|
||||||
|
popupDialog.css({
|
||||||
|
top: (top < 0? 0 : top) + 'px',
|
||||||
|
left: (left < 0? 0 : left) + 'px'
|
||||||
|
});
|
||||||
|
|
||||||
|
popupDialog.find('button.api-popup-cancel').click(function() {
|
||||||
|
popupMask.hide();
|
||||||
|
popupDialog.hide();
|
||||||
|
});
|
||||||
|
popupDialog.find('button.api-popup-authbtn').click(function() {
|
||||||
|
popupMask.hide();
|
||||||
|
popupDialog.hide();
|
||||||
|
|
||||||
|
var authSchemes = window.swaggerUi.api.authSchemes;
|
||||||
|
var host = window.location;
|
||||||
|
var redirectUrl = host.protocol + '//' + host.host + "/o2c.html";
|
||||||
|
var url = null;
|
||||||
|
|
||||||
|
var p = window.swaggerUi.api.authSchemes;
|
||||||
|
for (var key in p) {
|
||||||
|
if (p.hasOwnProperty(key)) {
|
||||||
|
var o = p[key].grantTypes;
|
||||||
|
for(var t in o) {
|
||||||
|
if(o.hasOwnProperty(t) && t === 'implicit') {
|
||||||
|
var dets = o[t];
|
||||||
|
url = dets.loginEndpoint.url + "?response_type=token";
|
||||||
|
window.swaggerUi.tokenName = dets.tokenName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var scopes = []
|
||||||
|
var o = $('.api-popup-scopes').find('input:checked');
|
||||||
|
|
||||||
|
for(k =0; k < o.length; k++) {
|
||||||
|
scopes.push($(o[k]).attr("scope"));
|
||||||
|
}
|
||||||
|
|
||||||
|
window.enabledScopes=scopes;
|
||||||
|
|
||||||
|
url += '&redirect_uri=' + encodeURIComponent(redirectUrl);
|
||||||
|
url += '&realm=' + encodeURIComponent(realm);
|
||||||
|
url += '&client_id=' + encodeURIComponent(clientId);
|
||||||
|
url += '&scope=' + encodeURIComponent(scopes);
|
||||||
|
|
||||||
|
window.open(url);
|
||||||
|
});
|
||||||
|
|
||||||
|
popupMask.show();
|
||||||
|
popupDialog.show();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function handleLogout() {
|
||||||
|
for(key in window.authorizations.authz){
|
||||||
|
window.authorizations.remove(key)
|
||||||
|
}
|
||||||
|
window.enabledScopes = null;
|
||||||
|
$('.api-ic.ic-on').addClass('ic-off');
|
||||||
|
$('.api-ic.ic-on').removeClass('ic-on');
|
||||||
|
|
||||||
|
// set the info box
|
||||||
|
$('.api-ic.ic-warning').addClass('ic-error');
|
||||||
|
$('.api-ic.ic-warning').removeClass('ic-warning');
|
||||||
|
}
|
||||||
|
|
||||||
|
function initOAuth(opts) {
|
||||||
|
var o = (opts||{});
|
||||||
|
var errors = [];
|
||||||
|
|
||||||
|
appName = (o.appName||errors.push("missing appName"));
|
||||||
|
popupMask = (o.popupMask||$('#api-common-mask'));
|
||||||
|
popupDialog = (o.popupDialog||$('.api-popup-dialog'));
|
||||||
|
clientId = (o.clientId||errors.push("missing client id"));
|
||||||
|
realm = (o.realm||errors.push("missing realm"));
|
||||||
|
|
||||||
|
if(errors.length > 0){
|
||||||
|
log("auth unable initialize oauth: " + errors);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$('pre code').each(function(i, e) {hljs.highlightBlock(e)});
|
||||||
|
$('.api-ic').click(function(s) {
|
||||||
|
if($(s.target).hasClass('ic-off'))
|
||||||
|
handleLogin();
|
||||||
|
else {
|
||||||
|
handleLogout();
|
||||||
|
}
|
||||||
|
false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function onOAuthComplete(token) {
|
||||||
|
if(token) {
|
||||||
|
if(token.error) {
|
||||||
|
var checkbox = $('input[type=checkbox],.secured')
|
||||||
|
checkbox.each(function(pos){
|
||||||
|
checkbox[pos].checked = false;
|
||||||
|
});
|
||||||
|
alert(token.error);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var b = token[window.swaggerUi.tokenName];
|
||||||
|
if(b){
|
||||||
|
// if all roles are satisfied
|
||||||
|
var o = null;
|
||||||
|
$.each($('.auth #api_information_panel'), function(k, v) {
|
||||||
|
var children = v;
|
||||||
|
if(children && children.childNodes) {
|
||||||
|
var requiredScopes = [];
|
||||||
|
$.each((children.childNodes), function (k1, v1){
|
||||||
|
var inner = v1.innerHTML;
|
||||||
|
if(inner)
|
||||||
|
requiredScopes.push(inner);
|
||||||
|
});
|
||||||
|
var diff = [];
|
||||||
|
for(var i=0; i < requiredScopes.length; i++) {
|
||||||
|
var s = requiredScopes[i];
|
||||||
|
if(window.enabledScopes && window.enabledScopes.indexOf(s) == -1) {
|
||||||
|
diff.push(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(diff.length > 0){
|
||||||
|
o = v.parentNode;
|
||||||
|
$(o.parentNode).find('.api-ic.ic-on').addClass('ic-off');
|
||||||
|
$(o.parentNode).find('.api-ic.ic-on').removeClass('ic-on');
|
||||||
|
|
||||||
|
// sorry, not all scopes are satisfied
|
||||||
|
$(o).find('.api-ic').addClass('ic-warning');
|
||||||
|
$(o).find('.api-ic').removeClass('ic-error');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
o = v.parentNode;
|
||||||
|
$(o.parentNode).find('.api-ic.ic-off').addClass('ic-on');
|
||||||
|
$(o.parentNode).find('.api-ic.ic-off').removeClass('ic-off');
|
||||||
|
|
||||||
|
// all scopes are satisfied
|
||||||
|
$(o).find('.api-ic').addClass('ic-info');
|
||||||
|
$(o).find('.api-ic').removeClass('ic-warning');
|
||||||
|
$(o).find('.api-ic').removeClass('ic-error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
window.authorizations.add("oauth2", new ApiKeyAuthorization("Authorization", "Bearer " + b, "header"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,32 @@
|
||||||
|
// Underscore.js 1.3.3
|
||||||
|
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
|
||||||
|
// Underscore is freely distributable under the MIT license.
|
||||||
|
// Portions of Underscore are inspired or borrowed from Prototype,
|
||||||
|
// Oliver Steele's Functional, and John Resig's Micro-Templating.
|
||||||
|
// For all details and documentation:
|
||||||
|
// http://documentcloud.github.com/underscore
|
||||||
|
(function(){function r(a,c,d){if(a===c)return 0!==a||1/a==1/c;if(null==a||null==c)return a===c;a._chain&&(a=a._wrapped);c._chain&&(c=c._wrapped);if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return!1;switch(e){case "[object String]":return a==""+c;case "[object Number]":return a!=+a?c!=+c:0==a?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source==
|
||||||
|
c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if("object"!=typeof a||"object"!=typeof c)return!1;for(var f=d.length;f--;)if(d[f]==a)return!0;d.push(a);var f=0,g=!0;if("[object Array]"==e){if(f=a.length,g=f==c.length)for(;f--&&(g=f in a==f in c&&r(a[f],c[f],d)););}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return!1;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&r(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c,h)&&!f--)break;
|
||||||
|
g=!f}}d.pop();return g}var s=this,I=s._,o={},k=Array.prototype,p=Object.prototype,i=k.slice,J=k.unshift,l=p.toString,K=p.hasOwnProperty,y=k.forEach,z=k.map,A=k.reduce,B=k.reduceRight,C=k.filter,D=k.every,E=k.some,q=k.indexOf,F=k.lastIndexOf,p=Array.isArray,L=Object.keys,t=Function.prototype.bind,b=function(a){return new m(a)};"undefined"!==typeof exports?("undefined"!==typeof module&&module.exports&&(exports=module.exports=b),exports._=b):s._=b;b.VERSION="1.3.3";var j=b.each=b.forEach=function(a,
|
||||||
|
c,d){if(a!=null)if(y&&a.forEach===y)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(e in a&&c.call(d,a[e],e,a)===o)break}else for(e in a)if(b.has(a,e)&&c.call(d,a[e],e,a)===o)break};b.map=b.collect=function(a,c,b){var e=[];if(a==null)return e;if(z&&a.map===z)return a.map(c,b);j(a,function(a,g,h){e[e.length]=c.call(b,a,g,h)});if(a.length===+a.length)e.length=a.length;return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(A&&
|
||||||
|
a.reduce===A){e&&(c=b.bind(c,e));return f?a.reduce(c,d):a.reduce(c)}j(a,function(a,b,i){if(f)d=c.call(e,d,a,b,i);else{d=a;f=true}});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(B&&a.reduceRight===B){e&&(c=b.bind(c,e));return f?a.reduceRight(c,d):a.reduceRight(c)}var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect=function(a,
|
||||||
|
c,b){var e;G(a,function(a,g,h){if(c.call(b,a,g,h)){e=a;return true}});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(C&&a.filter===C)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(D&&a.every===D)return a.every(c,b);j(a,function(a,g,h){if(!(e=e&&c.call(b,
|
||||||
|
a,g,h)))return o});return!!e};var G=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(E&&a.some===E)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return o});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;if(q&&a.indexOf===q)return a.indexOf(c)!=-1;return b=G(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck=
|
||||||
|
function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0])return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0])return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b<e.computed&&
|
||||||
|
(e={value:a,computed:b})});return e.value};b.shuffle=function(a){var b=[],d;j(a,function(a,f){d=Math.floor(Math.random()*(f+1));b[f]=b[d];b[d]=a});return b};b.sortBy=function(a,c,d){var e=b.isFunction(c)?c:function(a){return a[c]};return b.pluck(b.map(a,function(a,b,c){return{value:a,criteria:e.call(d,a,b,c)}}).sort(function(a,b){var c=a.criteria,d=b.criteria;return c===void 0?1:d===void 0?-1:c<d?-1:c>d?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};
|
||||||
|
j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a,c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:b.isArray(a)||b.isArguments(a)?i.call(a):a.toArray&&b.isFunction(a.toArray)?a.toArray():b.values(a)};b.size=function(a){return b.isArray(a)?a.length:b.keys(a).length};b.first=b.head=b.take=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};b.initial=function(a,b,d){return i.call(a,
|
||||||
|
0,a.length-(b==null||d?1:b))};b.last=function(a,b,d){return b!=null&&!d?i.call(a,Math.max(a.length-b,0)):a[a.length-1]};b.rest=b.tail=function(a,b,d){return i.call(a,b==null||d?1:b)};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a,c){return b.reduce(a,function(a,e){if(b.isArray(e))return a.concat(c?e:b.flatten(e));a[a.length]=e;return a},[])};b.without=function(a){return b.difference(a,i.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,
|
||||||
|
e=[];a.length<3&&(c=true);b.reduce(d,function(d,g,h){if(c?b.last(d)!==g||!d.length:!b.include(d,g)){d.push(g);e.push(a[h])}return d},[]);return e};b.union=function(){return b.uniq(b.flatten(arguments,true))};b.intersection=b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1),true);return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=
|
||||||
|
i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c,d){if(a==null)return-1;var e;if(d){d=b.sortedIndex(a,c);return a[d]===c?d:-1}if(q&&a.indexOf===q)return a.indexOf(c);d=0;for(e=a.length;d<e;d++)if(d in a&&a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(F&&a.lastIndexOf===F)return a.lastIndexOf(b);for(var d=a.length;d--;)if(d in a&&a[d]===b)return d;return-1};b.range=function(a,b,d){if(arguments.length<=
|
||||||
|
1){b=a||0;a=0}for(var d=arguments[2]||1,e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;){g[f++]=a;a=a+d}return g};var H=function(){};b.bind=function(a,c){var d,e;if(a.bind===t&&t)return t.apply(a,i.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=i.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(i.call(arguments)));H.prototype=a.prototype;var b=new H,g=a.apply(b,e.concat(i.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=
|
||||||
|
i.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,c){var d={};c||(c=b.identity);return function(){var e=c.apply(this,arguments);return b.has(d,e)?d[e]:d[e]=a.apply(this,arguments)}};b.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(null,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,h,i,j=b.debounce(function(){h=
|
||||||
|
g=false},c);return function(){d=this;e=arguments;f||(f=setTimeout(function(){f=null;h&&a.apply(d,e);j()},c));g?h=true:i=a.apply(d,e);j();g=true;return i}};b.debounce=function(a,b,d){var e;return function(){var f=this,g=arguments;d&&!e&&a.apply(f,g);clearTimeout(e);e=setTimeout(function(){e=null;d||a.apply(f,g)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=[a].concat(i.call(arguments,0));
|
||||||
|
return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=L||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&
|
||||||
|
c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.pick=function(a){var c={};j(b.flatten(i.call(arguments,1)),function(b){b in a&&(c[b]=a[b])});return c};b.defaults=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return r(a,b,[])};b.isEmpty=
|
||||||
|
function(a){if(a==null)return true;if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=p||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};b.isArguments=function(a){return l.call(a)=="[object Arguments]"};b.isArguments(arguments)||(b.isArguments=function(a){return!(!a||!b.has(a,"callee"))});b.isFunction=function(a){return l.call(a)=="[object Function]"};
|
||||||
|
b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isFinite=function(a){return b.isNumber(a)&&isFinite(a)};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"};b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,
|
||||||
|
b){return K.call(a,b)};b.noConflict=function(){s._=I;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.escape=function(a){return(""+a).replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};b.result=function(a,c){if(a==null)return null;var d=a[c];return b.isFunction(d)?d.call(a):d};b.mixin=function(a){j(b.functions(a),function(c){M(c,b[c]=a[c])})};var N=0;b.uniqueId=
|
||||||
|
function(a){var b=N++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var u=/.^/,n={"\\":"\\","'":"'",r:"\r",n:"\n",t:"\t",u2028:"\u2028",u2029:"\u2029"},v;for(v in n)n[n[v]]=v;var O=/\\|'|\r|\n|\t|\u2028|\u2029/g,P=/\\(\\|'|r|n|t|u2028|u2029)/g,w=function(a){return a.replace(P,function(a,b){return n[b]})};b.template=function(a,c,d){d=b.defaults(d||{},b.templateSettings);a="__p+='"+a.replace(O,function(a){return"\\"+n[a]}).replace(d.escape||
|
||||||
|
u,function(a,b){return"'+\n_.escape("+w(b)+")+\n'"}).replace(d.interpolate||u,function(a,b){return"'+\n("+w(b)+")+\n'"}).replace(d.evaluate||u,function(a,b){return"';\n"+w(b)+"\n;__p+='"})+"';\n";d.variable||(a="with(obj||{}){\n"+a+"}\n");var a="var __p='';var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n"+a+"return __p;\n",e=new Function(d.variable||"obj","_",a);if(c)return e(c,b);c=function(a){return e.call(this,a,b)};c.source="function("+(d.variable||"obj")+"){\n"+a+"}";return c};
|
||||||
|
b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var x=function(a,c){return c?b(a).chain():a},M=function(a,c){m.prototype[a]=function(){var a=i.call(arguments);J.call(a,this._wrapped);return x(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return x(d,
|
||||||
|
this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return x(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain=true;return this};m.prototype.value=function(){return this._wrapped}}).call(this);
|
|
@ -0,0 +1,78 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||||
|
<title>Matrix Client-Server API Documentation</title>
|
||||||
|
<link href="./files/css" rel="stylesheet" type="text/css">
|
||||||
|
<link href="./files/reset.css" media="screen" rel="stylesheet" type="text/css">
|
||||||
|
<link href="./files/screen.css" media="screen" rel="stylesheet" type="text/css">
|
||||||
|
<link href="./files/reset.css" media="print" rel="stylesheet" type="text/css">
|
||||||
|
<link href="./files/screen.css" media="print" rel="stylesheet" type="text/css">
|
||||||
|
<script type="text/javascript" src="./files/shred.bundle.js"></script>
|
||||||
|
<script src="./files/jquery-1.8.0.min.js" type="text/javascript"></script>
|
||||||
|
<script src="./files/jquery.slideto.min.js" type="text/javascript"></script>
|
||||||
|
<script src="./files/jquery.wiggle.min.js" type="text/javascript"></script>
|
||||||
|
<script src="./files/jquery.ba-bbq.min.js" type="text/javascript"></script>
|
||||||
|
<script src="./files/handlebars-1.0.0.js" type="text/javascript"></script>
|
||||||
|
<script src="./files/underscore-min.js" type="text/javascript"></script>
|
||||||
|
<script src="./files/backbone-min.js" type="text/javascript"></script>
|
||||||
|
<script src="./files/swagger.js" type="text/javascript"></script>
|
||||||
|
<script src="./files/swagger-ui.js" type="text/javascript"></script>
|
||||||
|
<script src="./files/highlight.7.3.pack.js" type="text/javascript"></script>
|
||||||
|
|
||||||
|
<!-- enabling this will enable oauth2 implicit scope support -->
|
||||||
|
<script src="./files/swagger-oauth.js" type="text/javascript"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(function () {
|
||||||
|
window.swaggerUi = new SwaggerUi({
|
||||||
|
url: "http://localhost:8000/swagger_matrix/api-docs",
|
||||||
|
dom_id: "swagger-ui-container",
|
||||||
|
supportedSubmitMethods: ['get', 'post', 'put', 'delete'],
|
||||||
|
onComplete: function(swaggerApi, swaggerUi){
|
||||||
|
log("Loaded SwaggerUI");
|
||||||
|
|
||||||
|
if(typeof initOAuth == "function") {
|
||||||
|
initOAuth({
|
||||||
|
clientId: "your-client-id",
|
||||||
|
realm: "your-realms",
|
||||||
|
appName: "your-app-name"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
$('pre code').each(function(i, e) {
|
||||||
|
hljs.highlightBlock(e)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onFailure: function(data) {
|
||||||
|
log("Unable to Load SwaggerUI");
|
||||||
|
},
|
||||||
|
docExpansion: "none"
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#input_apiKey').change(function() {
|
||||||
|
var key = $('#input_apiKey')[0].value;
|
||||||
|
log("key: " + key);
|
||||||
|
if(key && key.trim() != "") {
|
||||||
|
log("added key " + key);
|
||||||
|
window.authorizations.add("key", new ApiKeyAuthorization("access_token", key, "query"));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
window.swaggerUi.load();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body class="swagger-section">
|
||||||
|
<div id="header">
|
||||||
|
<div class="swagger-ui-wrap">
|
||||||
|
<a id="logo" href="http://swagger.wordnik.com/">swagger</a>
|
||||||
|
<form id="api_selector">
|
||||||
|
<div class="input"><input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl" type="text"></div>
|
||||||
|
<div class="input"><input placeholder="access_token" id="input_apiKey" name="apiKey" type="text"></div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="message-bar" class="swagger-ui-wrap message-fail">Can't read from server. It may not have the appropriate access-control-origin settings.</div>
|
||||||
|
<div id="swagger-ui-container" class="swagger-ui-wrap"></div>
|
||||||
|
|
||||||
|
|
||||||
|
</body></html>
|
|
@ -168,6 +168,10 @@ Some standard error codes are below:
|
||||||
:``M_NOT_FOUND``:
|
:``M_NOT_FOUND``:
|
||||||
No resource was found for this request.
|
No resource was found for this request.
|
||||||
|
|
||||||
|
:``M_LIMIT_EXCEEDED``:
|
||||||
|
Too many requests have been sent in a short period of time. Wait a while then
|
||||||
|
try again.
|
||||||
|
|
||||||
Some requests have unique error codes:
|
Some requests have unique error codes:
|
||||||
|
|
||||||
:``M_USER_IN_USE``:
|
:``M_USER_IN_USE``:
|
||||||
|
@ -273,6 +277,7 @@ Example::
|
||||||
}
|
}
|
||||||
|
|
||||||
- TODO: This creates a room creation event which serves as the root of the PDU graph for this room.
|
- TODO: This creates a room creation event which serves as the root of the PDU graph for this room.
|
||||||
|
- TODO: Keys for speccing a room name / room topic / invite these users?
|
||||||
|
|
||||||
Modifying aliases
|
Modifying aliases
|
||||||
-----------------
|
-----------------
|
||||||
|
@ -284,12 +289,37 @@ Permissions
|
||||||
|
|
||||||
Joining rooms
|
Joining rooms
|
||||||
-------------
|
-------------
|
||||||
- What is joining? What permissions / access does it give you? How does this affect /initialSync?
|
- TODO: What does the home server have to do to join a user to a room?
|
||||||
- API to hit (``/join/$alias or id``). Explain how alias joining works (auto-resolving). See "Room events" for more info.
|
|
||||||
- What does the home server have to do?
|
|
||||||
- Rooms that DON'T need an invite to join. This follows through onto inviting users section.
|
|
||||||
- Outline invite join dance?
|
|
||||||
|
|
||||||
|
Users need to join a room in order to send and receive events in that room. A user can join a
|
||||||
|
room by making a request to ``/join/<room alias or id>`` with::
|
||||||
|
|
||||||
|
{}
|
||||||
|
|
||||||
|
Alternatively, a user can make a request to ``/rooms/<room id>/join`` with the same request content.
|
||||||
|
This is only provided for symmetry with the other membership APIs: ``/rooms/<room id>/invite`` and
|
||||||
|
``/rooms/<room id>/leave``. If a room alias was specified, it will be automatically resolved to
|
||||||
|
a room ID, which will then be joined. The room ID that was joined will be returned in response::
|
||||||
|
|
||||||
|
{
|
||||||
|
"room_id": "!roomid:domain"
|
||||||
|
}
|
||||||
|
|
||||||
|
The membership state for the joining user can also be modified directly to be ``join``
|
||||||
|
by sending the following request to
|
||||||
|
``/rooms/<room id>/state/m.room.member/<url encoded user id>``::
|
||||||
|
|
||||||
|
{
|
||||||
|
"membership": "join"
|
||||||
|
}
|
||||||
|
|
||||||
|
See the "Room events" section for more information on ``m.room.member``.
|
||||||
|
|
||||||
|
After the user has joined a room, they will receive subsequent events in that room. This room
|
||||||
|
will now appear as an entry in the ``/initialSync`` API.
|
||||||
|
|
||||||
|
Some rooms enforce that a user is *invited* to a room before they can join that room. Other
|
||||||
|
rooms will allow anyone to join the room even if they have not received an invite.
|
||||||
|
|
||||||
Inviting users
|
Inviting users
|
||||||
--------------
|
--------------
|
||||||
|
@ -331,12 +361,33 @@ See the "Room events" section for more information on ``m.room.member``.
|
||||||
|
|
||||||
Leaving rooms
|
Leaving rooms
|
||||||
-------------
|
-------------
|
||||||
- API to hit (``$roomid/leave``). See "Room events" for more info.
|
A user can leave a room to stop receiving events for that room. A user must have
|
||||||
- Must be joined to leave. How does this affect /initialSync?
|
joined the room before they are eligible to leave the room. If the room is an
|
||||||
- Not ever being in a room is NOT equivalent to have left it (due to membership: leave).
|
"invite-only" room, they will need to be re-invited before they can re-join the room.
|
||||||
- Need to be re-invited if invite-only room.
|
To leave a room, a request should be made to ``/rooms/<room id>/leave`` with::
|
||||||
- If no more HSes in room, can delete room?
|
|
||||||
- Is there a dance?
|
{}
|
||||||
|
|
||||||
|
Alternatively, the membership state for this user in this room can be modified
|
||||||
|
directly by sending the following request to
|
||||||
|
``/rooms/<room id>/state/m.room.member/<url encoded user id>``::
|
||||||
|
|
||||||
|
{
|
||||||
|
"membership": "leave"
|
||||||
|
}
|
||||||
|
|
||||||
|
See the "Room events" section for more information on ``m.room.member``.
|
||||||
|
|
||||||
|
Once a user has left a room, that room will no longer appear on the ``/initialSync``
|
||||||
|
API. Be aware that leaving a room is not equivalent to have never been
|
||||||
|
in that room. A user who has previously left a room still maintains some residual state in
|
||||||
|
that room. Their membership state will be marked as ``leave``. This contrasts with
|
||||||
|
a user who has *never been invited or joined to that room* who will not have any
|
||||||
|
membership state for that room.
|
||||||
|
|
||||||
|
If all members in a room leave, that room becomes eligible for deletion.
|
||||||
|
- TODO: Grace period before deletion?
|
||||||
|
- TODO: Under what conditions should a room NOT be purged?
|
||||||
|
|
||||||
Events in a room
|
Events in a room
|
||||||
----------------
|
----------------
|
||||||
|
@ -350,7 +401,7 @@ Events in a room
|
||||||
|
|
||||||
Syncing a room
|
Syncing a room
|
||||||
--------------
|
--------------
|
||||||
- Single room initial sync. API to hit. Why it might be used (lazy loading)
|
- Single room initial sync. ``rooms/<room id>/initialSync`` API to hit. Why it might be used (lazy loading)
|
||||||
|
|
||||||
Getting grouped state events
|
Getting grouped state events
|
||||||
----------------------------
|
----------------------------
|
||||||
|
@ -378,6 +429,7 @@ Non-state messages
|
||||||
------------------
|
------------------
|
||||||
- m.room.message
|
- m.room.message
|
||||||
- m.room.message.feedback (and compressed format)
|
- m.room.message.feedback (and compressed format)
|
||||||
|
- voip?
|
||||||
|
|
||||||
What are they, when are they used, what do they contain, how should they be used
|
What are they, when are they used, what do they contain, how should they be used
|
||||||
|
|
||||||
|
@ -490,7 +542,7 @@ Each user has the concept of presence information. This encodes the
|
||||||
"availability" of that user, suitable for display on other user's clients. This
|
"availability" of that user, suitable for display on other user's clients. This
|
||||||
is transmitted as an ``m.presence`` event and is one of the few events which
|
is transmitted as an ``m.presence`` event and is one of the few events which
|
||||||
are sent *outside the context of a room*. The basic piece of presence information
|
are sent *outside the context of a room*. The basic piece of presence information
|
||||||
is represented by the ``state`` key, which is an enum of one of the following:
|
is represented by the ``presence`` key, which is an enum of one of the following:
|
||||||
|
|
||||||
- ``online`` : The default state when the user is connected to an event stream.
|
- ``online`` : The default state when the user is connected to an event stream.
|
||||||
- ``unavailable`` : The user is not reachable at this time.
|
- ``unavailable`` : The user is not reachable at this time.
|
||||||
|
@ -500,18 +552,18 @@ is represented by the ``state`` key, which is an enum of one of the following:
|
||||||
- ``hidden`` : TODO. Behaves as offline, but allows the user to see the client
|
- ``hidden`` : TODO. Behaves as offline, but allows the user to see the client
|
||||||
state anyway and generally interact with client features.
|
state anyway and generally interact with client features.
|
||||||
|
|
||||||
This basic ``state`` field applies to the user as a whole, regardless of how many
|
This basic ``presence`` field applies to the user as a whole, regardless of how many
|
||||||
client devices they have connected. The home server should synchronise this
|
client devices they have connected. The home server should synchronise this
|
||||||
status choice among multiple devices to ensure the user gets a consistent
|
status choice among multiple devices to ensure the user gets a consistent
|
||||||
experience.
|
experience.
|
||||||
|
|
||||||
Idle Time
|
Idle Time
|
||||||
---------
|
---------
|
||||||
As well as the basic ``state`` field, the presence information can also show a sense
|
As well as the basic ``presence`` field, the presence information can also show
|
||||||
of an "idle timer". This should be maintained individually by the user's
|
a sense of an "idle timer". This should be maintained individually by the
|
||||||
clients, and the home server can take the highest reported time as that to
|
user's clients, and the home server can take the highest reported time as that
|
||||||
report. When a user is offline, the home server can still report when the user was last
|
to report. When a user is offline, the home server can still report when the
|
||||||
seen online.
|
user was last seen online.
|
||||||
|
|
||||||
Transmission
|
Transmission
|
||||||
------------
|
------------
|
||||||
|
|
|
@ -52,6 +52,13 @@ def partitionbool(l, func):
|
||||||
|
|
||||||
class PresenceHandler(BaseHandler):
|
class PresenceHandler(BaseHandler):
|
||||||
|
|
||||||
|
STATE_LEVELS = {
|
||||||
|
PresenceState.OFFLINE: 0,
|
||||||
|
PresenceState.UNAVAILABLE: 1,
|
||||||
|
PresenceState.ONLINE: 2,
|
||||||
|
PresenceState.FREE_FOR_CHAT: 3,
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self, hs):
|
def __init__(self, hs):
|
||||||
super(PresenceHandler, self).__init__(hs)
|
super(PresenceHandler, self).__init__(hs)
|
||||||
|
|
||||||
|
@ -135,7 +142,7 @@ class PresenceHandler(BaseHandler):
|
||||||
return self._user_cachemap[user]
|
return self._user_cachemap[user]
|
||||||
else:
|
else:
|
||||||
statuscache = UserPresenceCache()
|
statuscache = UserPresenceCache()
|
||||||
statuscache.update({"state": PresenceState.OFFLINE}, user)
|
statuscache.update({"presence": PresenceState.OFFLINE}, user)
|
||||||
return statuscache
|
return statuscache
|
||||||
|
|
||||||
def registered_user(self, user):
|
def registered_user(self, user):
|
||||||
|
@ -173,19 +180,24 @@ class PresenceHandler(BaseHandler):
|
||||||
observed_user=target_user
|
observed_user=target_user
|
||||||
)
|
)
|
||||||
|
|
||||||
if visible:
|
if not visible:
|
||||||
state = yield self.store.get_presence_state(
|
|
||||||
target_user.localpart
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
raise SynapseError(404, "Presence information not visible")
|
raise SynapseError(404, "Presence information not visible")
|
||||||
|
state = yield self.store.get_presence_state(target_user.localpart)
|
||||||
|
if "mtime" in state:
|
||||||
|
del state["mtime"]
|
||||||
|
state["presence"] = state["state"]
|
||||||
|
|
||||||
|
if target_user in self._user_cachemap:
|
||||||
|
state["last_active"] = (
|
||||||
|
self._user_cachemap[target_user].get_state()["last_active"]
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
# TODO(paul): Have remote server send us permissions set
|
# TODO(paul): Have remote server send us permissions set
|
||||||
state = self._get_or_offline_usercache(target_user).get_state()
|
state = self._get_or_offline_usercache(target_user).get_state()
|
||||||
|
|
||||||
if "mtime" in state and (state["mtime"] is not None):
|
if "last_active" in state:
|
||||||
state["mtime_age"] = int(
|
state["last_active_ago"] = int(
|
||||||
self.clock.time_msec() - state.pop("mtime")
|
self.clock.time_msec() - state.pop("last_active")
|
||||||
)
|
)
|
||||||
defer.returnValue(state)
|
defer.returnValue(state)
|
||||||
|
|
||||||
|
@ -202,20 +214,33 @@ class PresenceHandler(BaseHandler):
|
||||||
if target_user != auth_user:
|
if target_user != auth_user:
|
||||||
raise AuthError(400, "Cannot set another user's displayname")
|
raise AuthError(400, "Cannot set another user's displayname")
|
||||||
|
|
||||||
# TODO(paul): Sanity-check 'state'
|
|
||||||
if "status_msg" not in state:
|
if "status_msg" not in state:
|
||||||
state["status_msg"] = None
|
state["status_msg"] = None
|
||||||
|
|
||||||
for k in state.keys():
|
for k in state.keys():
|
||||||
if k not in ("state", "status_msg"):
|
if k not in ("presence", "state", "status_msg"):
|
||||||
raise SynapseError(
|
raise SynapseError(
|
||||||
400, "Unexpected presence state key '%s'" % (k,)
|
400, "Unexpected presence state key '%s'" % (k,)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Handle legacy "state" key for now
|
||||||
|
if "state" in state:
|
||||||
|
state["presence"] = state.pop("state")
|
||||||
|
|
||||||
|
if state["presence"] not in self.STATE_LEVELS:
|
||||||
|
raise SynapseError(400, "'%s' is not a valid presence state" %
|
||||||
|
state["presence"]
|
||||||
|
)
|
||||||
|
|
||||||
logger.debug("Updating presence state of %s to %s",
|
logger.debug("Updating presence state of %s to %s",
|
||||||
target_user.localpart, state["state"])
|
target_user.localpart, state["presence"])
|
||||||
|
|
||||||
state_to_store = dict(state)
|
state_to_store = dict(state)
|
||||||
|
state_to_store["state"] = state_to_store.pop("presence")
|
||||||
|
|
||||||
|
statuscache=self._get_or_offline_usercache(target_user)
|
||||||
|
was_level = self.STATE_LEVELS[statuscache.get_state()["presence"]]
|
||||||
|
now_level = self.STATE_LEVELS[state["presence"]]
|
||||||
|
|
||||||
yield defer.DeferredList([
|
yield defer.DeferredList([
|
||||||
self.store.set_presence_state(
|
self.store.set_presence_state(
|
||||||
|
@ -226,9 +251,10 @@ class PresenceHandler(BaseHandler):
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
|
|
||||||
state["mtime"] = self.clock.time_msec()
|
if now_level > was_level:
|
||||||
|
state["last_active"] = self.clock.time_msec()
|
||||||
|
|
||||||
now_online = state["state"] != PresenceState.OFFLINE
|
now_online = state["presence"] != PresenceState.OFFLINE
|
||||||
was_polling = target_user in self._user_cachemap
|
was_polling = target_user in self._user_cachemap
|
||||||
|
|
||||||
if now_online and not was_polling:
|
if now_online and not was_polling:
|
||||||
|
@ -251,12 +277,12 @@ class PresenceHandler(BaseHandler):
|
||||||
@log_function
|
@log_function
|
||||||
def started_user_eventstream(self, user):
|
def started_user_eventstream(self, user):
|
||||||
# TODO(paul): Use "last online" state
|
# TODO(paul): Use "last online" state
|
||||||
self.set_state(user, user, {"state": PresenceState.ONLINE})
|
self.set_state(user, user, {"presence": PresenceState.ONLINE})
|
||||||
|
|
||||||
@log_function
|
@log_function
|
||||||
def stopped_user_eventstream(self, user):
|
def stopped_user_eventstream(self, user):
|
||||||
# TODO(paul): Save current state as "last online" state
|
# TODO(paul): Save current state as "last online" state
|
||||||
self.set_state(user, user, {"state": PresenceState.OFFLINE})
|
self.set_state(user, user, {"presence": PresenceState.OFFLINE})
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def user_joined_room(self, user, room_id):
|
def user_joined_room(self, user, room_id):
|
||||||
|
@ -385,9 +411,9 @@ class PresenceHandler(BaseHandler):
|
||||||
observed_user = self.hs.parse_userid(p.pop("observed_user_id"))
|
observed_user = self.hs.parse_userid(p.pop("observed_user_id"))
|
||||||
p["observed_user"] = observed_user
|
p["observed_user"] = observed_user
|
||||||
p.update(self._get_or_offline_usercache(observed_user).get_state())
|
p.update(self._get_or_offline_usercache(observed_user).get_state())
|
||||||
if "mtime" in p:
|
if "last_active" in p:
|
||||||
p["mtime_age"] = int(
|
p["last_active_ago"] = int(
|
||||||
self.clock.time_msec() - p.pop("mtime")
|
self.clock.time_msec() - p.pop("last_active")
|
||||||
)
|
)
|
||||||
|
|
||||||
defer.returnValue(presence)
|
defer.returnValue(presence)
|
||||||
|
@ -576,21 +602,30 @@ class PresenceHandler(BaseHandler):
|
||||||
def _push_presence_remote(self, user, destination, state=None):
|
def _push_presence_remote(self, user, destination, state=None):
|
||||||
if state is None:
|
if state is None:
|
||||||
state = yield self.store.get_presence_state(user.localpart)
|
state = yield self.store.get_presence_state(user.localpart)
|
||||||
|
del state["mtime"]
|
||||||
|
state["presence"] = state["state"]
|
||||||
|
|
||||||
|
if user in self._user_cachemap:
|
||||||
|
state["last_active"] = (
|
||||||
|
self._user_cachemap[user].get_state()["last_active"]
|
||||||
|
)
|
||||||
|
|
||||||
yield self.distributor.fire(
|
yield self.distributor.fire(
|
||||||
"collect_presencelike_data", user, state
|
"collect_presencelike_data", user, state
|
||||||
)
|
)
|
||||||
|
|
||||||
if "mtime" in state:
|
if "last_active" in state:
|
||||||
state = dict(state)
|
state = dict(state)
|
||||||
state["mtime_age"] = int(
|
state["last_active_ago"] = int(
|
||||||
self.clock.time_msec() - state.pop("mtime")
|
self.clock.time_msec() - state.pop("last_active")
|
||||||
)
|
)
|
||||||
|
|
||||||
user_state = {
|
user_state = {
|
||||||
"user_id": user.to_string(),
|
"user_id": user.to_string(),
|
||||||
}
|
}
|
||||||
user_state.update(**state)
|
user_state.update(**state)
|
||||||
|
if "state" in user_state and "presence" not in user_state:
|
||||||
|
user_state["presence"] = user_state["state"]
|
||||||
|
|
||||||
yield self.federation.send_edu(
|
yield self.federation.send_edu(
|
||||||
destination=destination,
|
destination=destination,
|
||||||
|
@ -622,9 +657,14 @@ class PresenceHandler(BaseHandler):
|
||||||
state = dict(push)
|
state = dict(push)
|
||||||
del state["user_id"]
|
del state["user_id"]
|
||||||
|
|
||||||
if "mtime_age" in state:
|
# Legacy handling
|
||||||
state["mtime"] = int(
|
if "presence" not in state:
|
||||||
self.clock.time_msec() - state.pop("mtime_age")
|
state["presence"] = state["state"]
|
||||||
|
del state["state"]
|
||||||
|
|
||||||
|
if "last_active_ago" in state:
|
||||||
|
state["last_active"] = int(
|
||||||
|
self.clock.time_msec() - state.pop("last_active_ago")
|
||||||
)
|
)
|
||||||
|
|
||||||
statuscache = self._get_or_make_usercache(user)
|
statuscache = self._get_or_make_usercache(user)
|
||||||
|
@ -639,7 +679,7 @@ class PresenceHandler(BaseHandler):
|
||||||
statuscache=statuscache,
|
statuscache=statuscache,
|
||||||
)
|
)
|
||||||
|
|
||||||
if state["state"] == PresenceState.OFFLINE:
|
if state["presence"] == PresenceState.OFFLINE:
|
||||||
del self._user_cachemap[user]
|
del self._user_cachemap[user]
|
||||||
|
|
||||||
for poll in content.get("poll", []):
|
for poll in content.get("poll", []):
|
||||||
|
@ -672,10 +712,9 @@ class PresenceHandler(BaseHandler):
|
||||||
yield defer.DeferredList(deferreds)
|
yield defer.DeferredList(deferreds)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def push_update_to_local_and_remote(self, observed_user,
|
def push_update_to_local_and_remote(self, observed_user, statuscache,
|
||||||
users_to_push=[], room_ids=[],
|
users_to_push=[], room_ids=[],
|
||||||
remote_domains=[],
|
remote_domains=[]):
|
||||||
statuscache=None):
|
|
||||||
|
|
||||||
localusers, remoteusers = partitionbool(
|
localusers, remoteusers = partitionbool(
|
||||||
users_to_push,
|
users_to_push,
|
||||||
|
@ -804,6 +843,7 @@ class UserPresenceCache(object):
|
||||||
|
|
||||||
def update(self, state, serial):
|
def update(self, state, serial):
|
||||||
assert("mtime_age" not in state)
|
assert("mtime_age" not in state)
|
||||||
|
assert("state" not in state)
|
||||||
|
|
||||||
self.state.update(state)
|
self.state.update(state)
|
||||||
# Delete keys that are now 'None'
|
# Delete keys that are now 'None'
|
||||||
|
@ -820,15 +860,21 @@ class UserPresenceCache(object):
|
||||||
|
|
||||||
def get_state(self):
|
def get_state(self):
|
||||||
# clone it so caller can't break our cache
|
# clone it so caller can't break our cache
|
||||||
return dict(self.state)
|
state = dict(self.state)
|
||||||
|
|
||||||
|
# Legacy handling
|
||||||
|
if "presence" in state:
|
||||||
|
state["state"] = state["presence"]
|
||||||
|
|
||||||
|
return state
|
||||||
|
|
||||||
def make_event(self, user, clock):
|
def make_event(self, user, clock):
|
||||||
content = self.get_state()
|
content = self.get_state()
|
||||||
content["user_id"] = user.to_string()
|
content["user_id"] = user.to_string()
|
||||||
|
|
||||||
if "mtime" in content:
|
if "last_active" in content:
|
||||||
content["mtime_age"] = int(
|
content["last_active_ago"] = int(
|
||||||
clock.time_msec() - content.pop("mtime")
|
clock.time_msec() - content.pop("last_active")
|
||||||
)
|
)
|
||||||
|
|
||||||
return {"type": "m.presence", "content": content}
|
return {"type": "m.presence", "content": content}
|
||||||
|
|
|
@ -48,7 +48,11 @@ class PresenceStatusRestServlet(RestServlet):
|
||||||
try:
|
try:
|
||||||
content = json.loads(request.content.read())
|
content = json.loads(request.content.read())
|
||||||
|
|
||||||
state["state"] = content.pop("state")
|
# Legacy handling
|
||||||
|
if "state" in content:
|
||||||
|
state["presence"] = content.pop("state")
|
||||||
|
else:
|
||||||
|
state["presence"] = content.pop("presence")
|
||||||
|
|
||||||
if "status_msg" in content:
|
if "status_msg" in content:
|
||||||
state["status_msg"] = content.pop("status_msg")
|
state["status_msg"] = content.pop("status_msg")
|
||||||
|
|
|
@ -35,8 +35,6 @@ ONLINE = PresenceState.ONLINE
|
||||||
|
|
||||||
|
|
||||||
logging.getLogger().addHandler(logging.NullHandler())
|
logging.getLogger().addHandler(logging.NullHandler())
|
||||||
#logging.getLogger().addHandler(logging.StreamHandler())
|
|
||||||
#logging.getLogger().setLevel(logging.DEBUG)
|
|
||||||
|
|
||||||
|
|
||||||
def _expect_edu(destination, edu_type, content, origin="test"):
|
def _expect_edu(destination, edu_type, content, origin="test"):
|
||||||
|
@ -141,7 +139,8 @@ class PresenceStateTestCase(unittest.TestCase):
|
||||||
target_user=self.u_apple, auth_user=self.u_apple
|
target_user=self.u_apple, auth_user=self.u_apple
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEquals({"state": ONLINE, "status_msg": "Online"},
|
self.assertEquals(
|
||||||
|
{"state": ONLINE, "presence": ONLINE, "status_msg": "Online"},
|
||||||
state
|
state
|
||||||
)
|
)
|
||||||
mocked_get.assert_called_with("apple")
|
mocked_get.assert_called_with("apple")
|
||||||
|
@ -157,7 +156,8 @@ class PresenceStateTestCase(unittest.TestCase):
|
||||||
target_user=self.u_apple, auth_user=self.u_banana
|
target_user=self.u_apple, auth_user=self.u_banana
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEquals({"state": ONLINE, "status_msg": "Online"},
|
self.assertEquals(
|
||||||
|
{"state": ONLINE, "presence": ONLINE, "status_msg": "Online"},
|
||||||
state
|
state
|
||||||
)
|
)
|
||||||
mocked_get.assert_called_with("apple")
|
mocked_get.assert_called_with("apple")
|
||||||
|
@ -175,7 +175,10 @@ class PresenceStateTestCase(unittest.TestCase):
|
||||||
target_user=self.u_apple, auth_user=self.u_clementine
|
target_user=self.u_apple, auth_user=self.u_clementine
|
||||||
)
|
)
|
||||||
|
|
||||||
self.assertEquals({"state": ONLINE, "status_msg": "Online"}, state)
|
self.assertEquals(
|
||||||
|
{"state": ONLINE, "presence": ONLINE, "status_msg": "Online"},
|
||||||
|
state
|
||||||
|
)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def test_get_disallowed_state(self):
|
def test_get_disallowed_state(self):
|
||||||
|
@ -202,20 +205,20 @@ class PresenceStateTestCase(unittest.TestCase):
|
||||||
|
|
||||||
yield self.handler.set_state(
|
yield self.handler.set_state(
|
||||||
target_user=self.u_apple, auth_user=self.u_apple,
|
target_user=self.u_apple, auth_user=self.u_apple,
|
||||||
state={"state": UNAVAILABLE, "status_msg": "Away"})
|
state={"presence": UNAVAILABLE, "status_msg": "Away"})
|
||||||
|
|
||||||
mocked_set.assert_called_with("apple",
|
mocked_set.assert_called_with("apple",
|
||||||
{"state": UNAVAILABLE, "status_msg": "Away"})
|
{"state": UNAVAILABLE, "status_msg": "Away"})
|
||||||
self.mock_start.assert_called_with(self.u_apple,
|
self.mock_start.assert_called_with(self.u_apple,
|
||||||
state={
|
state={
|
||||||
"state": UNAVAILABLE,
|
"presence": UNAVAILABLE,
|
||||||
"status_msg": "Away",
|
"status_msg": "Away",
|
||||||
"mtime": 1000000, # MockClock
|
"last_active": 1000000, # MockClock
|
||||||
})
|
})
|
||||||
|
|
||||||
yield self.handler.set_state(
|
yield self.handler.set_state(
|
||||||
target_user=self.u_apple, auth_user=self.u_apple,
|
target_user=self.u_apple, auth_user=self.u_apple,
|
||||||
state={"state": OFFLINE})
|
state={"presence": OFFLINE})
|
||||||
|
|
||||||
self.mock_stop.assert_called_with(self.u_apple)
|
self.mock_stop.assert_called_with(self.u_apple)
|
||||||
|
|
||||||
|
@ -449,28 +452,35 @@ class PresenceInvitesTestCase(unittest.TestCase):
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def test_get_presence_list(self):
|
def test_get_presence_list(self):
|
||||||
self.datastore.get_presence_list.return_value = defer.succeed(
|
self.datastore.get_presence_list.return_value = defer.succeed(
|
||||||
[{"observed_user_id": "@banana:test"}]
|
[{"observed_user_id": "@banana:test"}]
|
||||||
)
|
)
|
||||||
|
|
||||||
presence = yield self.handler.get_presence_list(
|
presence = yield self.handler.get_presence_list(
|
||||||
observer_user=self.u_apple)
|
observer_user=self.u_apple)
|
||||||
|
|
||||||
self.assertEquals([{"observed_user": self.u_banana,
|
self.assertEquals([
|
||||||
"state": OFFLINE}], presence)
|
{"observed_user": self.u_banana,
|
||||||
|
"presence": OFFLINE,
|
||||||
|
"state": OFFLINE},
|
||||||
|
], presence)
|
||||||
|
|
||||||
self.datastore.get_presence_list.assert_called_with("apple",
|
self.datastore.get_presence_list.assert_called_with("apple",
|
||||||
accepted=None)
|
accepted=None
|
||||||
|
)
|
||||||
|
|
||||||
self.datastore.get_presence_list.return_value = defer.succeed(
|
self.datastore.get_presence_list.return_value = defer.succeed(
|
||||||
[{"observed_user_id": "@banana:test"}]
|
[{"observed_user_id": "@banana:test"}]
|
||||||
)
|
)
|
||||||
|
|
||||||
presence = yield self.handler.get_presence_list(
|
presence = yield self.handler.get_presence_list(
|
||||||
observer_user=self.u_apple, accepted=True)
|
observer_user=self.u_apple, accepted=True
|
||||||
|
)
|
||||||
|
|
||||||
self.assertEquals([{"observed_user": self.u_banana,
|
self.assertEquals([
|
||||||
"state": OFFLINE}], presence)
|
{"observed_user": self.u_banana,
|
||||||
|
"presence": OFFLINE,
|
||||||
|
"state": OFFLINE},
|
||||||
|
], presence)
|
||||||
|
|
||||||
self.datastore.get_presence_list.assert_called_with("apple",
|
self.datastore.get_presence_list.assert_called_with("apple",
|
||||||
accepted=True)
|
accepted=True)
|
||||||
|
@ -611,6 +621,9 @@ class PresencePushTestCase(unittest.TestCase):
|
||||||
|
|
||||||
# TODO(paul): Gut-wrenching
|
# TODO(paul): Gut-wrenching
|
||||||
self.handler._user_cachemap[self.u_apple] = UserPresenceCache()
|
self.handler._user_cachemap[self.u_apple] = UserPresenceCache()
|
||||||
|
self.handler._user_cachemap[self.u_apple].update(
|
||||||
|
{"presence": OFFLINE}, serial=0
|
||||||
|
)
|
||||||
apple_set = self.handler._local_pushmap.setdefault("apple", set())
|
apple_set = self.handler._local_pushmap.setdefault("apple", set())
|
||||||
apple_set.add(self.u_banana)
|
apple_set.add(self.u_banana)
|
||||||
apple_set.add(self.u_clementine)
|
apple_set.add(self.u_clementine)
|
||||||
|
@ -618,7 +631,8 @@ class PresencePushTestCase(unittest.TestCase):
|
||||||
self.assertEquals(self.event_source.get_current_key(), 0)
|
self.assertEquals(self.event_source.get_current_key(), 0)
|
||||||
|
|
||||||
yield self.handler.set_state(self.u_apple, self.u_apple,
|
yield self.handler.set_state(self.u_apple, self.u_apple,
|
||||||
{"state": ONLINE})
|
{"presence": ONLINE}
|
||||||
|
)
|
||||||
|
|
||||||
self.assertEquals(self.event_source.get_current_key(), 1)
|
self.assertEquals(self.event_source.get_current_key(), 1)
|
||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
|
@ -627,8 +641,9 @@ class PresencePushTestCase(unittest.TestCase):
|
||||||
{"type": "m.presence",
|
{"type": "m.presence",
|
||||||
"content": {
|
"content": {
|
||||||
"user_id": "@apple:test",
|
"user_id": "@apple:test",
|
||||||
|
"presence": ONLINE,
|
||||||
"state": ONLINE,
|
"state": ONLINE,
|
||||||
"mtime_age": 0,
|
"last_active_ago": 0,
|
||||||
}},
|
}},
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -636,13 +651,21 @@ class PresencePushTestCase(unittest.TestCase):
|
||||||
presence = yield self.handler.get_presence_list(
|
presence = yield self.handler.get_presence_list(
|
||||||
observer_user=self.u_apple, accepted=True)
|
observer_user=self.u_apple, accepted=True)
|
||||||
|
|
||||||
self.assertEquals([
|
self.assertEquals(
|
||||||
{"observed_user": self.u_banana, "state": OFFLINE},
|
[
|
||||||
{"observed_user": self.u_clementine, "state": OFFLINE}],
|
{"observed_user": self.u_banana,
|
||||||
presence)
|
"presence": OFFLINE,
|
||||||
|
"state": OFFLINE},
|
||||||
|
{"observed_user": self.u_clementine,
|
||||||
|
"presence": OFFLINE,
|
||||||
|
"state": OFFLINE},
|
||||||
|
],
|
||||||
|
presence
|
||||||
|
)
|
||||||
|
|
||||||
yield self.handler.set_state(self.u_banana, self.u_banana,
|
yield self.handler.set_state(self.u_banana, self.u_banana,
|
||||||
{"state": ONLINE})
|
{"presence": ONLINE}
|
||||||
|
)
|
||||||
|
|
||||||
self.clock.advance_time(2)
|
self.clock.advance_time(2)
|
||||||
|
|
||||||
|
@ -651,9 +674,11 @@ class PresencePushTestCase(unittest.TestCase):
|
||||||
|
|
||||||
self.assertEquals([
|
self.assertEquals([
|
||||||
{"observed_user": self.u_banana,
|
{"observed_user": self.u_banana,
|
||||||
|
"presence": ONLINE,
|
||||||
"state": ONLINE,
|
"state": ONLINE,
|
||||||
"mtime_age": 2000},
|
"last_active_ago": 2000},
|
||||||
{"observed_user": self.u_clementine,
|
{"observed_user": self.u_clementine,
|
||||||
|
"presence": OFFLINE,
|
||||||
"state": OFFLINE},
|
"state": OFFLINE},
|
||||||
], presence)
|
], presence)
|
||||||
|
|
||||||
|
@ -666,8 +691,9 @@ class PresencePushTestCase(unittest.TestCase):
|
||||||
{"type": "m.presence",
|
{"type": "m.presence",
|
||||||
"content": {
|
"content": {
|
||||||
"user_id": "@banana:test",
|
"user_id": "@banana:test",
|
||||||
|
"presence": ONLINE,
|
||||||
"state": ONLINE,
|
"state": ONLINE,
|
||||||
"mtime_age": 2000
|
"last_active_ago": 2000
|
||||||
}},
|
}},
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -682,8 +708,9 @@ class PresencePushTestCase(unittest.TestCase):
|
||||||
content={
|
content={
|
||||||
"push": [
|
"push": [
|
||||||
{"user_id": "@apple:test",
|
{"user_id": "@apple:test",
|
||||||
|
"presence": u"online",
|
||||||
"state": u"online",
|
"state": u"online",
|
||||||
"mtime_age": 0},
|
"last_active_ago": 0},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -699,11 +726,14 @@ class PresencePushTestCase(unittest.TestCase):
|
||||||
|
|
||||||
# TODO(paul): Gut-wrenching
|
# TODO(paul): Gut-wrenching
|
||||||
self.handler._user_cachemap[self.u_apple] = UserPresenceCache()
|
self.handler._user_cachemap[self.u_apple] = UserPresenceCache()
|
||||||
|
self.handler._user_cachemap[self.u_apple].update(
|
||||||
|
{"presence": OFFLINE}, serial=0
|
||||||
|
)
|
||||||
apple_set = self.handler._remote_sendmap.setdefault("apple", set())
|
apple_set = self.handler._remote_sendmap.setdefault("apple", set())
|
||||||
apple_set.add(self.u_potato.domain)
|
apple_set.add(self.u_potato.domain)
|
||||||
|
|
||||||
yield self.handler.set_state(self.u_apple, self.u_apple,
|
yield self.handler.set_state(self.u_apple, self.u_apple,
|
||||||
{"state": ONLINE}
|
{"presence": ONLINE}
|
||||||
)
|
)
|
||||||
|
|
||||||
yield put_json.await_calls()
|
yield put_json.await_calls()
|
||||||
|
@ -726,7 +756,7 @@ class PresencePushTestCase(unittest.TestCase):
|
||||||
"push": [
|
"push": [
|
||||||
{"user_id": "@potato:remote",
|
{"user_id": "@potato:remote",
|
||||||
"state": "online",
|
"state": "online",
|
||||||
"mtime_age": 1000},
|
"last_active_ago": 1000},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -741,8 +771,9 @@ class PresencePushTestCase(unittest.TestCase):
|
||||||
{"type": "m.presence",
|
{"type": "m.presence",
|
||||||
"content": {
|
"content": {
|
||||||
"user_id": "@potato:remote",
|
"user_id": "@potato:remote",
|
||||||
|
"presence": ONLINE,
|
||||||
"state": ONLINE,
|
"state": ONLINE,
|
||||||
"mtime_age": 1000,
|
"last_active_ago": 1000,
|
||||||
}}
|
}}
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -751,7 +782,10 @@ class PresencePushTestCase(unittest.TestCase):
|
||||||
|
|
||||||
state = yield self.handler.get_state(self.u_potato, self.u_apple)
|
state = yield self.handler.get_state(self.u_potato, self.u_apple)
|
||||||
|
|
||||||
self.assertEquals({"state": ONLINE, "mtime_age": 3000}, state)
|
self.assertEquals(
|
||||||
|
{"state": ONLINE, "presence": ONLINE, "last_active_ago": 3000},
|
||||||
|
state
|
||||||
|
)
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def test_join_room_local(self):
|
def test_join_room_local(self):
|
||||||
|
@ -763,8 +797,8 @@ class PresencePushTestCase(unittest.TestCase):
|
||||||
self.handler._user_cachemap[self.u_clementine] = UserPresenceCache()
|
self.handler._user_cachemap[self.u_clementine] = UserPresenceCache()
|
||||||
self.handler._user_cachemap[self.u_clementine].update(
|
self.handler._user_cachemap[self.u_clementine].update(
|
||||||
{
|
{
|
||||||
"state": PresenceState.ONLINE,
|
"presence": PresenceState.ONLINE,
|
||||||
"mtime": self.clock.time_msec(),
|
"last_active": self.clock.time_msec(),
|
||||||
}, self.u_clementine
|
}, self.u_clementine
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -781,8 +815,9 @@ class PresencePushTestCase(unittest.TestCase):
|
||||||
{"type": "m.presence",
|
{"type": "m.presence",
|
||||||
"content": {
|
"content": {
|
||||||
"user_id": "@clementine:test",
|
"user_id": "@clementine:test",
|
||||||
|
"presence": ONLINE,
|
||||||
"state": ONLINE,
|
"state": ONLINE,
|
||||||
"mtime_age": 0,
|
"last_active_ago": 0,
|
||||||
}}
|
}}
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
@ -798,7 +833,8 @@ class PresencePushTestCase(unittest.TestCase):
|
||||||
content={
|
content={
|
||||||
"push": [
|
"push": [
|
||||||
{"user_id": "@apple:test",
|
{"user_id": "@apple:test",
|
||||||
"state": "online"},
|
"presence": "online",
|
||||||
|
"state": "online"},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
@ -812,7 +848,8 @@ class PresencePushTestCase(unittest.TestCase):
|
||||||
content={
|
content={
|
||||||
"push": [
|
"push": [
|
||||||
{"user_id": "@banana:test",
|
{"user_id": "@banana:test",
|
||||||
"state": "offline"},
|
"presence": "offline",
|
||||||
|
"state": "offline"},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
@ -823,7 +860,7 @@ class PresencePushTestCase(unittest.TestCase):
|
||||||
# TODO(paul): Gut-wrenching
|
# TODO(paul): Gut-wrenching
|
||||||
self.handler._user_cachemap[self.u_apple] = UserPresenceCache()
|
self.handler._user_cachemap[self.u_apple] = UserPresenceCache()
|
||||||
self.handler._user_cachemap[self.u_apple].update(
|
self.handler._user_cachemap[self.u_apple].update(
|
||||||
{"state": PresenceState.ONLINE}, self.u_apple)
|
{"presence": PresenceState.ONLINE}, self.u_apple)
|
||||||
self.room_members = [self.u_apple, self.u_banana]
|
self.room_members = [self.u_apple, self.u_banana]
|
||||||
|
|
||||||
yield self.distributor.fire("user_joined_room", self.u_potato,
|
yield self.distributor.fire("user_joined_room", self.u_potato,
|
||||||
|
@ -841,7 +878,8 @@ class PresencePushTestCase(unittest.TestCase):
|
||||||
content={
|
content={
|
||||||
"push": [
|
"push": [
|
||||||
{"user_id": "@clementine:test",
|
{"user_id": "@clementine:test",
|
||||||
"state": "online"},
|
"presence": "online",
|
||||||
|
"state": "online"},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
@ -851,7 +889,7 @@ class PresencePushTestCase(unittest.TestCase):
|
||||||
|
|
||||||
self.handler._user_cachemap[self.u_clementine] = UserPresenceCache()
|
self.handler._user_cachemap[self.u_clementine] = UserPresenceCache()
|
||||||
self.handler._user_cachemap[self.u_clementine].update(
|
self.handler._user_cachemap[self.u_clementine].update(
|
||||||
{"state": ONLINE}, self.u_clementine)
|
{"presence": ONLINE}, self.u_clementine)
|
||||||
self.room_members.append(self.u_potato)
|
self.room_members.append(self.u_potato)
|
||||||
|
|
||||||
yield self.distributor.fire("user_joined_room", self.u_clementine,
|
yield self.distributor.fire("user_joined_room", self.u_clementine,
|
||||||
|
@ -935,7 +973,8 @@ class PresencePollingTestCase(unittest.TestCase):
|
||||||
def get_presence_state(user_localpart):
|
def get_presence_state(user_localpart):
|
||||||
return defer.succeed(
|
return defer.succeed(
|
||||||
{"state": self.current_user_state[user_localpart],
|
{"state": self.current_user_state[user_localpart],
|
||||||
"status_msg": None}
|
"status_msg": None,
|
||||||
|
"mtime": 123456000}
|
||||||
)
|
)
|
||||||
self.datastore.get_presence_state = get_presence_state
|
self.datastore.get_presence_state = get_presence_state
|
||||||
|
|
||||||
|
@ -969,7 +1008,7 @@ class PresencePollingTestCase(unittest.TestCase):
|
||||||
# apple goes online
|
# apple goes online
|
||||||
yield self.handler.set_state(
|
yield self.handler.set_state(
|
||||||
target_user=self.u_apple, auth_user=self.u_apple,
|
target_user=self.u_apple, auth_user=self.u_apple,
|
||||||
state={"state": ONLINE}
|
state={"presence": ONLINE}
|
||||||
)
|
)
|
||||||
|
|
||||||
# apple should see both banana and clementine currently offline
|
# apple should see both banana and clementine currently offline
|
||||||
|
@ -992,8 +1031,9 @@ class PresencePollingTestCase(unittest.TestCase):
|
||||||
|
|
||||||
# banana goes online
|
# banana goes online
|
||||||
yield self.handler.set_state(
|
yield self.handler.set_state(
|
||||||
target_user=self.u_banana, auth_user=self.u_banana,
|
target_user=self.u_banana, auth_user=self.u_banana,
|
||||||
state={"state": ONLINE})
|
state={"presence": ONLINE}
|
||||||
|
)
|
||||||
|
|
||||||
# apple and banana should now both see each other online
|
# apple and banana should now both see each other online
|
||||||
self.mock_update_client.assert_has_calls([
|
self.mock_update_client.assert_has_calls([
|
||||||
|
@ -1013,8 +1053,9 @@ class PresencePollingTestCase(unittest.TestCase):
|
||||||
|
|
||||||
# apple goes offline
|
# apple goes offline
|
||||||
yield self.handler.set_state(
|
yield self.handler.set_state(
|
||||||
target_user=self.u_apple, auth_user=self.u_apple,
|
target_user=self.u_apple, auth_user=self.u_apple,
|
||||||
state={"state": OFFLINE})
|
state={"presence": OFFLINE}
|
||||||
|
)
|
||||||
|
|
||||||
# banana should now be told apple is offline
|
# banana should now be told apple is offline
|
||||||
self.mock_update_client.assert_has_calls([
|
self.mock_update_client.assert_has_calls([
|
||||||
|
@ -1027,7 +1068,6 @@ class PresencePollingTestCase(unittest.TestCase):
|
||||||
self.assertFalse("banana" in self.handler._local_pushmap)
|
self.assertFalse("banana" in self.handler._local_pushmap)
|
||||||
self.assertFalse("clementine" in self.handler._local_pushmap)
|
self.assertFalse("clementine" in self.handler._local_pushmap)
|
||||||
|
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def test_remote_poll_send(self):
|
def test_remote_poll_send(self):
|
||||||
put_json = self.mock_http_client.put_json
|
put_json = self.mock_http_client.put_json
|
||||||
|
@ -1057,8 +1097,9 @@ class PresencePollingTestCase(unittest.TestCase):
|
||||||
|
|
||||||
# clementine goes online
|
# clementine goes online
|
||||||
yield self.handler.set_state(
|
yield self.handler.set_state(
|
||||||
target_user=self.u_clementine, auth_user=self.u_clementine,
|
target_user=self.u_clementine, auth_user=self.u_clementine,
|
||||||
state={"state": ONLINE})
|
state={"presence": ONLINE}
|
||||||
|
)
|
||||||
|
|
||||||
yield put_json.await_calls()
|
yield put_json.await_calls()
|
||||||
|
|
||||||
|
@ -1085,7 +1126,7 @@ class PresencePollingTestCase(unittest.TestCase):
|
||||||
# fig goes online; shouldn't send a second poll
|
# fig goes online; shouldn't send a second poll
|
||||||
yield self.handler.set_state(
|
yield self.handler.set_state(
|
||||||
target_user=self.u_fig, auth_user=self.u_fig,
|
target_user=self.u_fig, auth_user=self.u_fig,
|
||||||
state={"state": ONLINE}
|
state={"presence": ONLINE}
|
||||||
)
|
)
|
||||||
|
|
||||||
# reactor.iterate(delay=0)
|
# reactor.iterate(delay=0)
|
||||||
|
@ -1095,7 +1136,7 @@ class PresencePollingTestCase(unittest.TestCase):
|
||||||
# fig goes offline
|
# fig goes offline
|
||||||
yield self.handler.set_state(
|
yield self.handler.set_state(
|
||||||
target_user=self.u_fig, auth_user=self.u_fig,
|
target_user=self.u_fig, auth_user=self.u_fig,
|
||||||
state={"state": OFFLINE}
|
state={"presence": OFFLINE}
|
||||||
)
|
)
|
||||||
|
|
||||||
reactor.iterate(delay=0)
|
reactor.iterate(delay=0)
|
||||||
|
@ -1116,8 +1157,9 @@ class PresencePollingTestCase(unittest.TestCase):
|
||||||
|
|
||||||
# clementine goes offline
|
# clementine goes offline
|
||||||
yield self.handler.set_state(
|
yield self.handler.set_state(
|
||||||
target_user=self.u_clementine, auth_user=self.u_clementine,
|
target_user=self.u_clementine, auth_user=self.u_clementine,
|
||||||
state={"state": OFFLINE})
|
state={"presence": OFFLINE}
|
||||||
|
)
|
||||||
|
|
||||||
yield put_json.await_calls()
|
yield put_json.await_calls()
|
||||||
|
|
||||||
|
@ -1135,6 +1177,7 @@ class PresencePollingTestCase(unittest.TestCase):
|
||||||
content={
|
content={
|
||||||
"push": [
|
"push": [
|
||||||
{"user_id": "@banana:test",
|
{"user_id": "@banana:test",
|
||||||
|
"presence": "offline",
|
||||||
"state": "offline",
|
"state": "offline",
|
||||||
"status_msg": None},
|
"status_msg": None},
|
||||||
],
|
],
|
||||||
|
|
|
@ -166,7 +166,11 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
|
||||||
# TODO(paul): Gut-wrenching
|
# TODO(paul): Gut-wrenching
|
||||||
from synapse.handlers.presence import UserPresenceCache
|
from synapse.handlers.presence import UserPresenceCache
|
||||||
self.handlers.presence_handler._user_cachemap[self.u_apple] = (
|
self.handlers.presence_handler._user_cachemap[self.u_apple] = (
|
||||||
UserPresenceCache())
|
UserPresenceCache()
|
||||||
|
)
|
||||||
|
self.handlers.presence_handler._user_cachemap[self.u_apple].update(
|
||||||
|
{"presence": OFFLINE}, serial=0
|
||||||
|
)
|
||||||
apple_set = self.handlers.presence_handler._local_pushmap.setdefault(
|
apple_set = self.handlers.presence_handler._local_pushmap.setdefault(
|
||||||
"apple", set())
|
"apple", set())
|
||||||
apple_set.add(self.u_banana)
|
apple_set.add(self.u_banana)
|
||||||
|
@ -182,11 +186,13 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
|
||||||
|
|
||||||
self.assertEquals([
|
self.assertEquals([
|
||||||
{"observed_user": self.u_banana,
|
{"observed_user": self.u_banana,
|
||||||
|
"presence": ONLINE,
|
||||||
"state": ONLINE,
|
"state": ONLINE,
|
||||||
"mtime_age": 0,
|
"last_active_ago": 0,
|
||||||
"displayname": "Frank",
|
"displayname": "Frank",
|
||||||
"avatar_url": "http://foo"},
|
"avatar_url": "http://foo"},
|
||||||
{"observed_user": self.u_clementine,
|
{"observed_user": self.u_clementine,
|
||||||
|
"presence": OFFLINE,
|
||||||
"state": OFFLINE}],
|
"state": OFFLINE}],
|
||||||
presence)
|
presence)
|
||||||
|
|
||||||
|
@ -199,8 +205,8 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
|
||||||
|
|
||||||
statuscache = self.mock_update_client.call_args[1]["statuscache"]
|
statuscache = self.mock_update_client.call_args[1]["statuscache"]
|
||||||
self.assertEquals({
|
self.assertEquals({
|
||||||
"state": ONLINE,
|
"presence": ONLINE,
|
||||||
"mtime": 1000000, # MockClock
|
"last_active": 1000000, # MockClock
|
||||||
"displayname": "Frank",
|
"displayname": "Frank",
|
||||||
"avatar_url": "http://foo",
|
"avatar_url": "http://foo",
|
||||||
}, statuscache.state)
|
}, statuscache.state)
|
||||||
|
@ -222,8 +228,8 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
|
||||||
|
|
||||||
statuscache = self.mock_update_client.call_args[1]["statuscache"]
|
statuscache = self.mock_update_client.call_args[1]["statuscache"]
|
||||||
self.assertEquals({
|
self.assertEquals({
|
||||||
"state": ONLINE,
|
"presence": ONLINE,
|
||||||
"mtime": 1000000, # MockClock
|
"last_active": 1000000, # MockClock
|
||||||
"displayname": "I am an Apple",
|
"displayname": "I am an Apple",
|
||||||
"avatar_url": "http://foo",
|
"avatar_url": "http://foo",
|
||||||
}, statuscache.state)
|
}, statuscache.state)
|
||||||
|
@ -241,7 +247,11 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
|
||||||
# TODO(paul): Gut-wrenching
|
# TODO(paul): Gut-wrenching
|
||||||
from synapse.handlers.presence import UserPresenceCache
|
from synapse.handlers.presence import UserPresenceCache
|
||||||
self.handlers.presence_handler._user_cachemap[self.u_apple] = (
|
self.handlers.presence_handler._user_cachemap[self.u_apple] = (
|
||||||
UserPresenceCache())
|
UserPresenceCache()
|
||||||
|
)
|
||||||
|
self.handlers.presence_handler._user_cachemap[self.u_apple].update(
|
||||||
|
{"presence": OFFLINE}, serial=0
|
||||||
|
)
|
||||||
apple_set = self.handlers.presence_handler._remote_sendmap.setdefault(
|
apple_set = self.handlers.presence_handler._remote_sendmap.setdefault(
|
||||||
"apple", set())
|
"apple", set())
|
||||||
apple_set.add(self.u_potato.domain)
|
apple_set.add(self.u_potato.domain)
|
||||||
|
@ -255,8 +265,9 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
|
||||||
content={
|
content={
|
||||||
"push": [
|
"push": [
|
||||||
{"user_id": "@apple:test",
|
{"user_id": "@apple:test",
|
||||||
|
"presence": "online",
|
||||||
"state": "online",
|
"state": "online",
|
||||||
"mtime_age": 0,
|
"last_active_ago": 0,
|
||||||
"displayname": "Frank",
|
"displayname": "Frank",
|
||||||
"avatar_url": "http://foo"},
|
"avatar_url": "http://foo"},
|
||||||
],
|
],
|
||||||
|
@ -293,14 +304,16 @@ class PresenceProfilelikeDataTestCase(unittest.TestCase):
|
||||||
statuscache=ANY)
|
statuscache=ANY)
|
||||||
|
|
||||||
statuscache = self.mock_update_client.call_args[1]["statuscache"]
|
statuscache = self.mock_update_client.call_args[1]["statuscache"]
|
||||||
self.assertEquals({"state": ONLINE,
|
self.assertEquals({"presence": ONLINE,
|
||||||
"displayname": "Frank",
|
"displayname": "Frank",
|
||||||
"avatar_url": "http://foo"}, statuscache.state)
|
"avatar_url": "http://foo"}, statuscache.state)
|
||||||
|
|
||||||
state = yield self.handlers.presence_handler.get_state(self.u_potato,
|
state = yield self.handlers.presence_handler.get_state(self.u_potato,
|
||||||
self.u_apple)
|
self.u_apple)
|
||||||
|
|
||||||
self.assertEquals({"state": ONLINE,
|
self.assertEquals(
|
||||||
"displayname": "Frank",
|
{"presence": ONLINE,
|
||||||
"avatar_url": "http://foo"},
|
"state": ONLINE,
|
||||||
state)
|
"displayname": "Frank",
|
||||||
|
"avatar_url": "http://foo"},
|
||||||
|
state)
|
||||||
|
|
|
@ -98,8 +98,10 @@ class PresenceStateTestCase(unittest.TestCase):
|
||||||
"/presence/%s/status" % (myid), None)
|
"/presence/%s/status" % (myid), None)
|
||||||
|
|
||||||
self.assertEquals(200, code)
|
self.assertEquals(200, code)
|
||||||
self.assertEquals({"state": ONLINE, "status_msg": "Available"},
|
self.assertEquals(
|
||||||
response)
|
{"presence": ONLINE, "state": ONLINE, "status_msg": "Available"},
|
||||||
|
response
|
||||||
|
)
|
||||||
mocked_get.assert_called_with("apple")
|
mocked_get.assert_called_with("apple")
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
|
@ -109,7 +111,7 @@ class PresenceStateTestCase(unittest.TestCase):
|
||||||
|
|
||||||
(code, response) = yield self.mock_resource.trigger("PUT",
|
(code, response) = yield self.mock_resource.trigger("PUT",
|
||||||
"/presence/%s/status" % (myid),
|
"/presence/%s/status" % (myid),
|
||||||
'{"state": "unavailable", "status_msg": "Away"}')
|
'{"presence": "unavailable", "status_msg": "Away"}')
|
||||||
|
|
||||||
self.assertEquals(200, code)
|
self.assertEquals(200, code)
|
||||||
mocked_set.assert_called_with("apple",
|
mocked_set.assert_called_with("apple",
|
||||||
|
@ -173,9 +175,9 @@ class PresenceListTestCase(unittest.TestCase):
|
||||||
"/presence/list/%s" % (myid), None)
|
"/presence/list/%s" % (myid), None)
|
||||||
|
|
||||||
self.assertEquals(200, code)
|
self.assertEquals(200, code)
|
||||||
self.assertEquals(
|
self.assertEquals([
|
||||||
[{"user_id": "@banana:test", "state": OFFLINE}], response
|
{"user_id": "@banana:test", "presence": OFFLINE, "state": OFFLINE},
|
||||||
)
|
], response)
|
||||||
|
|
||||||
self.datastore.get_presence_list.assert_called_with(
|
self.datastore.get_presence_list.assert_called_with(
|
||||||
"apple", accepted=True
|
"apple", accepted=True
|
||||||
|
@ -314,7 +316,8 @@ class PresenceEventStreamTestCase(unittest.TestCase):
|
||||||
[])
|
[])
|
||||||
|
|
||||||
yield self.presence.set_state(self.u_banana, self.u_banana,
|
yield self.presence.set_state(self.u_banana, self.u_banana,
|
||||||
state={"state": ONLINE})
|
state={"presence": ONLINE}
|
||||||
|
)
|
||||||
|
|
||||||
(code, response) = yield self.mock_resource.trigger("GET",
|
(code, response) = yield self.mock_resource.trigger("GET",
|
||||||
"/events?from=0_1_0&timeout=0", None)
|
"/events?from=0_1_0&timeout=0", None)
|
||||||
|
@ -324,8 +327,9 @@ class PresenceEventStreamTestCase(unittest.TestCase):
|
||||||
{"type": "m.presence",
|
{"type": "m.presence",
|
||||||
"content": {
|
"content": {
|
||||||
"user_id": "@banana:test",
|
"user_id": "@banana:test",
|
||||||
|
"presence": ONLINE,
|
||||||
"state": ONLINE,
|
"state": ONLINE,
|
||||||
"displayname": "Frank",
|
"displayname": "Frank",
|
||||||
"mtime_age": 0,
|
"last_active_ago": 0,
|
||||||
}},
|
}},
|
||||||
]}, response)
|
]}, response)
|
||||||
|
|
|
@ -201,9 +201,15 @@ angular.module('RoomController', ['ngSanitize', 'mFileInput'])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// selectively update membership else it will nuke the picture and displayname too :/
|
// selectively update membership and presence else it will nuke the picture and displayname too :/
|
||||||
var member = $scope.members[target_user_id];
|
var member = $scope.members[target_user_id];
|
||||||
member.content.membership = chunk.content.membership;
|
member.membership = chunk.content.membership;
|
||||||
|
if ("state" in chunk.content) {
|
||||||
|
member.presenceState = chunk.content.state;
|
||||||
|
}
|
||||||
|
if ("mtime_age" in chunk.content) {
|
||||||
|
member.mtime_age = chunk.content.mtime_age;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -331,6 +337,11 @@ angular.module('RoomController', ['ngSanitize', 'mFileInput'])
|
||||||
// Make sure the initialSync has been before going further
|
// Make sure the initialSync has been before going further
|
||||||
eventHandlerService.waitForInitialSyncCompletion().then(
|
eventHandlerService.waitForInitialSyncCompletion().then(
|
||||||
function() {
|
function() {
|
||||||
|
|
||||||
|
// Some data has been retrieved from the iniialSync request
|
||||||
|
// So, the relative time starts here
|
||||||
|
$scope.now = new Date().getTime();
|
||||||
|
|
||||||
var needsToJoin = true;
|
var needsToJoin = true;
|
||||||
|
|
||||||
// The room members is available in the data fetched by initialSync
|
// The room members is available in the data fetched by initialSync
|
||||||
|
@ -377,10 +388,22 @@ angular.module('RoomController', ['ngSanitize', 'mFileInput'])
|
||||||
|
|
||||||
// Make recents highlight the current room
|
// Make recents highlight the current room
|
||||||
$scope.recentsSelectedRoomID = $scope.room_id;
|
$scope.recentsSelectedRoomID = $scope.room_id;
|
||||||
|
|
||||||
|
// Get the up-to-date the current member list
|
||||||
|
matrixService.getMemberList($scope.room_id).then(
|
||||||
|
function(response) {
|
||||||
|
for (var i = 0; i < response.data.chunk.length; i++) {
|
||||||
|
var chunk = response.data.chunk[i];
|
||||||
|
updateMemberList(chunk);
|
||||||
|
updateMemberListPresenceAge();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
function(error) {
|
||||||
|
$scope.feedback = "Failed get member list: " + error.data.error;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
paginate(MESSAGES_PER_PAGINATION);
|
paginate(MESSAGES_PER_PAGINATION);
|
||||||
|
|
||||||
updateMemberListPresenceAge();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.inviteUser = function(user_id) {
|
$scope.inviteUser = function(user_id) {
|
||||||
|
|
|
@ -106,7 +106,7 @@
|
||||||
<button ng-click="answerCall()">Answer</button>
|
<button ng-click="answerCall()">Answer</button>
|
||||||
<button ng-click="hangupCall()">Reject</button>
|
<button ng-click="hangupCall()">Reject</button>
|
||||||
</div>
|
</div>
|
||||||
<button ng-click="hangupCall()" ng-show="currentCall && currentCall.state != 'ringing'">Hang up</button>
|
<button ng-click="hangupCall()" ng-show="currentCall && currentCall.state != 'ringing' && currentCall.state != 'ended' && currentCall.state != 'fledgling'">Hang up</button>
|
||||||
<span ng-show="currentCall.state == 'invite_sent'">Calling...</span>
|
<span ng-show="currentCall.state == 'invite_sent'">Calling...</span>
|
||||||
<span ng-show="currentCall.state == 'connecting'">Call Connecting...</span>
|
<span ng-show="currentCall.state == 'connecting'">Call Connecting...</span>
|
||||||
<span ng-show="currentCall.state == 'connected'">Call Connected</span>
|
<span ng-show="currentCall.state == 'connected'">Call Connected</span>
|
||||||
|
|
Loading…
Reference in New Issue