diff --git a/.htaccess b/.htaccess new file mode 100644 index 0000000..b1c38b0 --- /dev/null +++ b/.htaccess @@ -0,0 +1 @@ +AddType application/json .json diff --git a/README b/README index a45b210..edd4ebd 100644 --- a/README +++ b/README @@ -1,3 +1,12 @@ -This repository does contain a few specifications about publishing -activity streams. - \ No newline at end of file +This repository contains the JSON activity streams specification version 1.0 + +It also contains json-schema documents which map to all the constructs in the spec. + +The syntax of the schema is defined at +http://json-schema.org/ + +To test you can use the following library: +https://github.com/Constellation/ruby-jsonchema + +More libraries available in http://json-schema.org/ + diff --git a/schema/activity.json b/schema/activity.json new file mode 100644 index 0000000..1b13c09 --- /dev/null +++ b/schema/activity.json @@ -0,0 +1,85 @@ +{ + "type" : "object", + "title" : "activity", + "description" : "An activity construct recounts what an actor did to an object in the past. If there is no actor it simply describes the change.", + "properties": { + "id" :{ + "type" : "string", + "description" : "Uniquely identifies each activity within the service", + "default" :"{url}" + }, + "actor" : { + "type": "object", + "required": true, + "description": "Describes the entity that performed the activity. An activity MUST contain one actor property whose value is a single Object.", + "properties" : {"$ref":"./object.json#properties"} + }, + "verb" : { + "title" : "verb", + "type" : "string", + "default": "post", + "required": true, + "description" :"Identifies the action that the activity describes. An activity MUST contain a verb property whose value is a JSON String that is non-empty and matches either the \"isegment-nz-nc\" or the \"IRI\" production in [RFC3987]. Note that the use of a relative reference other than a simple name is not allowed." + }, + "object" : { + "type": "object", + "required" : true, + "description" : "Describes the primary object of the activity. For instance, in the activity, \"John saved a movie to his wishlist\", the object of the activity is \"movie\". An activity SHOULD contain an object property whose value is a single Object. If the object property is not contained, the primary object of the activity MAY be implied by context.", + "properties" : {"$ref":"./object.json#properties"} + }, + "target" : { + "type": "object", + "description" : "Describes the target of the activity. The precise meaning of the activity's target is dependent on the activities verb, but will often be the object the English preposition \"to\". For instance, in the activity, \"John saved a movie to his wishlist\", the target of the activity is \"wishlist\". The activity target MUST NOT be used to identity an indirect object that is not a target of the activity. An activity MAY contain a target property whose value is a single Object.", + "properties" : {"$ref":"./object.json#properties"} + }, + "published" : { + "type" : "string", + "description": "The date and time at which the activity occurred. It is important to note that this is not necessarily the same as the time at which the activity was published. An activity MUST contain a postedTime property.", + "format" : "date-time" + }, + "updated" : { + "type" : "string", + "description": "The date and time at which a previously published activity has been modified. An Activity MAY contain an updatedTime property", + "format" : "date-time" + }, + "generator" : { + "type": "object", + "description": "Describes the application that generated the activity. An activity MAY contain a generator property whose value is a single Object.", + "properties" : {"$ref":"./object.json#properties"} + }, + "icon" : { + "type": "object", + "properties" :{"$ref":"./media_link.json#properties"}, + "description": "An IRI[RFC3987] identifying an image resource provides a visual representation of the activity, intended for human consumption. The image SHOULD have an aspect ratio of one (horizontal) to one (vertical) and SHOULD be suitable for presentation at a small size. An activity MAY have an icon property" + }, + "provider" : { + "type": "object", + "description" : "Describes the application that published the activity. Note that this is not necessarily the same entity that generated the activity. An activity MAY contain a provider property whose value is a single Object", + "properties" : {"$ref":"./object.json#properties"} + }, + "title" : { + "type" : "string", + "description" : "Natural-language title or headline for the activity encoded as a single JSON String containing HTML markup. An activity MAY contain a title property", + "format": "html" + }, + "content" : { + "type" : "string", + "description" : "Natural-language description of the activity encoded as a single JSON String containing HTML markup. Visual elements such as thumbnail images MAY be included. An activity MAY contain a content property", + "format" : "html" + }, + "url" : { + "type" : "string", + "description" : "An IRI [RFC3987] identifying a resource providing an HTML representation of the activity. An activity MAY contain a url property", + "format" : "url" + }, + "links" : { + "type" : "array", + "description" :"Links between this object and other resources as defined in Web Linking", + "note": "Tell JSON schema team to not put links inside http://json-schema.org/hyper-schema#properties", + "properties" : {"$ref" : "http://json-schema.org/links#properties"} + } + }, + "links": [ + {"rel": "describedby", "href": "./verbs/{verb}.json"} + ] +} diff --git a/schema/collection.json b/schema/collection.json new file mode 100644 index 0000000..0406c4b --- /dev/null +++ b/schema/collection.json @@ -0,0 +1,33 @@ +{ + "type" : "object", + "title" : "collection", + "description" : "A collection is a generic list of Objects of any object type. The objectType of each item in the collection MAY be omitted if the type of object can be established through context. The collection is used primarily as the root of an Activity Streams document, but can be used as the value of extension properties in a variety of situations. ", + "properties" : { + "url" : { + "type" : "string", + "description": "An IRI [RFC3987] referencing a JSON document containing the full listing of objects in the collection." + }, + "totalItems": { + "type" : "number", + "description" : "Non-negative integer specifying the total number of activities within the stream. The Stream serialization MAY contain a count property." + }, + "items" : { + "type" : "array", + "required" : true, + "description" : "An array containing a listing of Objects of any object type. If used in combination with the url property, the items array can be used to provide a subset of the objects that may be found in the resource identified by the url.", + "items": { + "type" : "object", + "properties" :{"$ref":"./object.json#properties"} + } + }, + "links" : { + "type" : "array", + "optional" : true, + "description" :"Links between an this object and other resources as defined in Web Linking", + "properties" : {"$ref" : "http://json-schema.org/links#properties"} + } + }, + "links": [ + {"rel": "describedby", "href": "http://json-schema.org/schema"} + ] +} \ No newline at end of file diff --git a/schema/library/jsonschema-b4.js b/schema/library/jsonschema-b4.js new file mode 100644 index 0000000..a9fffbe --- /dev/null +++ b/schema/library/jsonschema-b4.js @@ -0,0 +1,225 @@ +/** + * JSONSchema Validator - Validates JavaScript objects using JSON Schemas + * (http://www.json.com/json-schema-proposal/) + * + * Copyright (c) 2007 Kris Zyp SitePen (www.sitepen.com) + * Updated by: Monica Wilkinson on 07/23/2010 + * + * Licensed under the MIT (MIT-LICENSE.txt) license. +To use the validator call JSONSchema.validate with an instance object and an optional schema object. +If a schema is provided, it will be used to validate. If the instance object refers to a schema (self-validating), +that schema will be used to validate and the schema parameter is not necessary (if both exist, +both validations will occur). +The validate method will return an array of validation errors. If there are no errors, then an +empty list will be returned. A validation error will have two properties: +"property" which indicates which property had the error +"message" which indicates what the error was + */ + +JSONSchema = { + validate : function(/*Any*/instance,/*Object*/schema) { + // Summary: + // To use the validator call JSONSchema.validate with an instance object and an optional schema object. + // If a schema is provided, it will be used to validate. If the instance object refers to a schema (self-validating), + // that schema will be used to validate and the schema parameter is not necessary (if both exist, + // both validations will occur). + // The validate method will return an object with two properties: + // valid: A boolean indicating if the instance is valid by the schema + // errors: An array of validation errors. If there are no errors, then an + // empty list will be returned. A validation error will have two properties: + // property: which indicates which property had the error + // message: which indicates what the error was + // + return this._validate(instance,schema,false); + }, + checkPropertyChange : function(/*Any*/value,/*Object*/schema, /*String*/ property) { + // Summary: + // The checkPropertyChange method will check to see if an value can legally be in property with the given schema + // This is slightly different than the validate method in that it will fail if the schema is readonly and it will + // not check for self-validation, it is assumed that the passed in value is already internally valid. + // The checkPropertyChange method will return the same object type as validate, see JSONSchema.validate for + // information. + // + return this._validate(value,schema, property || "property"); + }, + _validate : function(/*Any*/instance,/*Object*/schema,/*Boolean*/ _changing) { + + var errors = []; + // validate a value against a property definition + function checkProp(value, schema, path,i){ + var l; + path += path ? typeof i == 'number' ? '[' + i + ']' : typeof i == 'undefined' ? '' : '.' + i : i; + function addError(message){ + errors.push({property:path,message:message}); + } + + if((typeof schema != 'object' || schema instanceof Array) && (path || typeof schema != 'function')){ + if(typeof schema == 'function'){ + if(!(value instanceof schema)){ + addError("is not an instance of the class/constructor " + schema.name); + } + }else if(schema){ + addError("Invalid schema/property definition " + schema); + } + return null; + } + if(_changing && schema.readonly){ + addError("is a readonly field, it can not be changed"); + } + if(schema['extends']){ // if it extends another schema, it must pass that schema as well + checkProp(value,schema['extends'],path,i); + } + // validate a value against a type definition + function checkType(type,value){ + if(type){ + if(typeof type == 'string' && type != 'any' && + (type == 'null' ? value !== null : typeof value != type) && + !(value instanceof Array && type == 'array') && + !(type == 'integer' && value%1===0)){ + return [{property:path,message:(typeof value) + " value found, but a " + type + " is required"}]; + } + if(type instanceof Array){ + var unionErrors=[]; + for(var j = 0; j < type.length; j++){ // a union type + if(!(unionErrors=checkType(type[j],value)).length){ + break; + } + } + if(unionErrors.length){ + return unionErrors; + } + }else if(typeof type == 'object'){ + var priorErrors = errors; + errors = []; + checkProp(value,type,path); + var theseErrors = errors; + errors = priorErrors; + return theseErrors; + } + } + return []; + } + // Monica: Added code to check the default + if(value === undefined){ + value = schema["default"]; + } + if(value === undefined){ + if(!schema.optional){ + addError("is missing and it is not optional"); + } + }else{ + errors = errors.concat(checkType(schema.type,value)); + if(schema.disallow && !checkType(schema.disallow,value).length){ + addError(" disallowed value was matched"); + } + if(value !== null){ + if(value instanceof Array){ + if(schema.items){ + if(schema.items instanceof Array){ + for(i=0,l=value.length; i schema.maxItems){ + addError("There must be a maximum of " + schema.maxItems + " in the array"); + } + }else if(schema.properties){ + errors.concat(checkObj(value,schema.properties,path,schema.additionalProperties)); + } + if(schema.pattern && typeof value == 'string' && !value.match(schema.pattern)){ + addError("does not match the regex pattern " + schema.pattern); + } + if(schema.maxLength && typeof value == 'string' && value.length > schema.maxLength){ + addError("may only be " + schema.maxLength + " characters long"); + } + if(schema.minLength && typeof value == 'string' && value.length < schema.minLength){ + addError("must be at least " + schema.minLength + " characters long"); + } + if(typeof schema.minimum !== undefined && typeof value == typeof schema.minimum && + schema.minimum > value){ + addError("must have a minimum value of " + schema.minimum); + } + if(typeof schema.maximum !== undefined && typeof value == typeof schema.maximum && + schema.maximum < value){ + addError("must have a maximum value of " + schema.maximum); + } + if(schema['enum']){ + var enumer = schema['enum']; + l = enumer.length; + var found; + for(var j = 0; j < l; j++){ + if(enumer[j]===value){ + found=1; + break; + } + } + if(!found){ + addError("does not have a value in the enumeration " + enumer.join(", ")); + } + } + if(typeof schema.maxDecimal == 'number' && + (value.toString().match(new RegExp("\\.[0-9]{" + (schema.maxDecimal + 1) + ",}")))){ + addError("may only have " + schema.maxDecimal + " digits of decimal places"); + } + } + + } + return null; + } + // validate an object against a schema + function checkObj(instance,objTypeDef,path,additionalProp){ + + if(typeof objTypeDef =='object'){ + if(typeof instance != 'object' || instance instanceof Array){ + errors.push({property:path,message:"an object is required"}); + } + + // check all the properties are present as required in the schema + for(var i in objTypeDef){ + if(objTypeDef.hasOwnProperty(i) && !(i.charAt(0) == '_' && i.charAt(1) == '_')){ + var value = instance[i]; + var propDef = objTypeDef[i]; + checkProp(value,propDef,path,i); + } + } + } + + // check the properties of the instance + for(i in instance){ + if(instance.hasOwnProperty(i) && !(i.charAt(0) == '_' && i.charAt(1) == '_') && objTypeDef && !objTypeDef[i] && additionalProp===false){ + errors.push({property:path,message:(typeof value) + "The property " + i + + " is not defined in the schema and the schema does not allow additional properties"}); + } + var requires = objTypeDef && objTypeDef[i] && objTypeDef[i].requires; + if(requires && !(requires in instance)){ + errors.push({property:path,message:"the presence of the property " + i + " requires that " + requires + " also be present"}); + } + value = instance[i]; + + if(!_changing && value && value.$schema){ + errors = errors.concat(checkProp(value,value.$schema,path,i)); + } + } + return errors; + } + if(schema){ + checkProp(instance,schema,'',_changing || ''); + } + if(!_changing && instance && instance.$schema){ + checkProp(instance,instance.$schema,'',''); + } + return {valid:!errors.length,errors:errors}; + } + /* will add this later + newFromSchema : function() { + } +*/ +} diff --git a/schema/library/ref.js b/schema/library/ref.js new file mode 100644 index 0000000..800af6a --- /dev/null +++ b/schema/library/ref.js @@ -0,0 +1,349 @@ +dojo.provide("dojox.json.ref"); +dojo.require("dojo.date.stamp"); + +// summary: +// Adds advanced JSON {de}serialization capabilities to the base json library. +// This enhances the capabilities of dojo.toJson and dojo.fromJson, +// adding referencing support, date handling, and other extra format handling. +// On parsing, references are resolved. When references are made to +// ids/objects that have been loaded yet, the loader function will be set to +// _loadObject to denote a lazy loading (not loaded yet) object. + +dojox.json.ref = { + resolveJson: function(/*Object*/ root,/*Object?*/ args){ + // summary: + // Indexes and resolves references in the JSON object. + // description: + // A JSON Schema object that can be used to advise the handling of the JSON (defining ids, date properties, urls, etc) + // + // root: + // The root object of the object graph to be processed + // args: + // Object with additional arguments: + // + // The *index* parameter. + // This is the index object (map) to use to store an index of all the objects. + // If you are using inter-message referencing, you must provide the same object for each call. + // The *defaultId* parameter. + // This is the default id to use for the root object (if it doesn't define it's own id) + // The *idPrefix* parameter. + // This the prefix to use for the ids as they enter the index. This allows multiple tables + // to use ids (that might otherwise collide) that enter the same global index. + // idPrefix should be in the form "/Service/". For example, + // if the idPrefix is "/Table/", and object is encountered {id:"4",...}, this would go in the + // index as "/Table/4". + // The *idAttribute* parameter. + // This indicates what property is the identity property. This defaults to "id" + // The *assignAbsoluteIds* parameter. + // This indicates that the resolveJson should assign absolute ids (__id) as the objects are being parsed. + // + // The *schemas* parameter + // This provides a map of schemas, from which prototypes can be retrieved + // The *loader* parameter + // This is a function that is called added to the reference objects that can't be resolved (lazy objects) + // return: + // An object, the result of the processing + args = args || {}; + var idAttribute = args.idAttribute || 'id'; + var prefix = args.idPrefix || ''; + var assignAbsoluteIds = args.assignAbsoluteIds; + var index = args.index || {}; // create an index if one doesn't exist + var timeStamps = args.timeStamps; + var ref,reWalk=[]; + var pathResolveRegex = /^(.*\/)?(\w+:\/\/)|[^\/\.]+\/\.\.\/|^.*\/(\/)/; + var addProp = this._addProp; + var F = function(){}; + + + function getExternal(url, path) { + var handle = dojo.xhrGet({"url" : url, "sync" : true, "handleAs" : "text"}); + var txt = handle.results.toString(); + txt = txt.substring(0, txt.length-2); + txt = txt + ", '_find' : {'$ref' : '#" + path + "'}}"; + var json = dojox.json.ref.fromJson(txt); + return json["_find"]; + } + + function walk(it, stop, defaultId, schema, defaultObject){ + // this walks the new graph, resolving references and making other changes + var update, val, id = idAttribute in it ? it[idAttribute] : defaultId; + if(id !== undefined){ + id = (prefix + id).replace(pathResolveRegex,'$2$3'); + } + var target = defaultObject || it; + if(id !== undefined){ // if there is an id available... + if(assignAbsoluteIds){ + it.__id = id; + } + if(args.schemas && (!(it instanceof Array)) && // won't try on arrays to do prototypes, plus it messes with queries + (val = id.match(/^(.+\/)[^\.\[]*$/))){ // if it has a direct table id (no paths) + schema = args.schemas[val[1]]; + } + // if the id already exists in the system, we should use the existing object, and just + // update it... as long as the object is compatible + if(index[id] && ((it instanceof Array) == (index[id] instanceof Array))){ + target = index[id]; + delete target.$ref; // remove this artifact + update = true; + }else{ + var proto = schema && schema.prototype; // and if has a prototype + if(proto){ + // if the schema defines a prototype, that needs to be the prototype of the object + F.prototype = proto; + target = new F(); + } + } + index[id] = target; // add the prefix, set _id, and index it + if(timeStamps){ + timeStamps[id] = args.time; + } + } + var properties = schema && schema.properties; + var length = it.length; + for(var i in it){ + if(i==length){ + break; + } + if(it.hasOwnProperty(i)){ + val=it[i]; + var propertyDefinition = properties && properties[i]; + if(propertyDefinition && propertyDefinition.format == 'date-time' && typeof val == 'string'){ + val = dojo.date.stamp.fromISOString(val); + }else if((typeof val =='object') && val && !(val instanceof Date)){ + ref=val.$ref; + if(ref){ // a reference was found + // make sure it is a safe reference + delete it[i];// remove the property so it doesn't resolve to itself in the case of id.propertyName lazy values + + var path = ref.toString(); + // Monica: Adding support for external references + var parts = path.split('#'); + if (parts.length == 2 && parts[0].length>0) { + ref = getExternal(parts[0], parts[1]); + } + else { + path = path.replace(/(#)([^\.\[])/,'$1.$2').match(/(^([^\[]*\/)?[^#\.\[]*)#?([\.\[].*)?/); // divide along the path + if((ref = (path[1]=='$' || path[1]=='this' || path[1]=='') ? root : index[(prefix + path[1]).replace(pathResolveRegex,'$2$3')])){ // a $ indicates to start with the root, otherwise start with an id + // if there is a path, we will iterate through the path references + if(path[3]){ + path[3].replace(/(\[([^\]]+)\])|(\.?([^\.\[]+))/g,function(t,a,b,c,d){ + ref = ref && ref[b ? b.replace(/[\"\'\\]/,'') : d]; + }); + } + } + } + if(ref){ + val = ref; + }else{ + // otherwise, no starting point was found (id not found), if stop is set, it does not exist, we have + // unloaded reference, if stop is not set, it may be in a part of the graph not walked yet, + // we will wait for the second loop + if(!stop){ + var rewalking; + if(!rewalking){ + reWalk.push(target); // we need to rewalk it to resolve references + } + rewalking = true; // we only want to add it once + val = walk(val, false, val.$ref, propertyDefinition); + }else{ + // create a lazy loaded object + val._loadObject = args.loader; + } + } + }else{ + if(!stop){ // if we are in stop, that means we are in the second loop, and we only need to check this current one, + // further walking may lead down circular loops + val = walk( + val, + reWalk==it, + id && addProp(id, i), // the default id to use + propertyDefinition, + // if we have an existing object child, we want to + // maintain it's identity, so we pass it as the default object + target != it && typeof target[i] == 'object' && target[i] + ); + } + } + } + it[i] = val; + if(target!=it && !target.__isDirty){// do updates if we are updating an existing object and it's not dirty + var old = target[i]; + target[i] = val; // only update if it changed + if(update && val !== old && // see if it is different + !target._loadObject && // no updates if we are just lazy loading + !(val instanceof Date && old instanceof Date && val.getTime() == old.getTime()) && // make sure it isn't an identical date + !(typeof val == 'function' && typeof old == 'function' && val.toString() == old.toString()) && // make sure it isn't an indentical function + index.onUpdate){ + index.onUpdate(target,i,old,val); // call the listener for each update + } + } + } + } + + if(update){ + // this means we are updating, we need to remove deleted + for(i in target){ + if(!target.__isDirty && target.hasOwnProperty(i) && !it.hasOwnProperty(i) && i != '__id' && i != '__clientId' && !(target instanceof Array && isNaN(i))){ + if(index.onUpdate && i != "_loadObject" && i != "_idAttr"){ + index.onUpdate(target,i,target[i],undefined); // call the listener for each update + } + delete target[i]; + while(target instanceof Array && target.length && target[target.length-1] === undefined){ + // shorten the target if necessary + target.length--; + } + } + } + }else{ + if(index.onLoad){ + index.onLoad(target); + } + } + return target; + } + if(root && typeof root == 'object'){ + root = walk(root,false,args.defaultId); // do the main walk through + walk(reWalk,false); // re walk any parts that were not able to resolve references on the first round + } + return root; + }, + + + fromJson: function(/*String*/ str,/*Object?*/ args){ + // summary: + // evaluates the passed string-form of a JSON object. + // + // str: + // a string literal of a JSON item, for instance: + // '{ "foo": [ "bar", 1, { "baz": "thud" } ] }' + // args: See resolveJson + // + // return: + // An object, the result of the evaluation + function ref(target){ // support call styles references as well + return {$ref:target}; + } + try{ + var root = eval('(' + str + ')'); // do the eval + }catch(e){ + throw new SyntaxError("Invalid JSON string: " + e.message + " parsing: "+ str); + } + if(root){ + return this.resolveJson(root, args); + } + return root; + }, + + toJson: function(/*Object*/ it, /*Boolean?*/ prettyPrint, /*Object?*/ idPrefix, /*Object?*/ indexSubObjects){ + // summary: + // Create a JSON serialization of an object. + // This has support for referencing, including circular references, duplicate references, and out-of-message references + // id and path-based referencing is supported as well and is based on http://www.json.com/2007/10/19/json-referencing-proposal-and-library/. + // + // it: + // an object to be serialized. + // + // prettyPrint: + // if true, we indent objects and arrays to make the output prettier. + // The variable dojo.toJsonIndentStr is used as the indent string + // -- to use something other than the default (tab), + // change that variable before calling dojo.toJson(). + // + // idPrefix: The prefix that has been used for the absolute ids + // + // return: + // a String representing the serialized version of the passed object. + var useRefs = this._useRefs; + var addProp = this._addProp; + idPrefix = idPrefix || ''; // the id prefix for this context + var paths={}; + var generated = {}; + function serialize(it,path,_indentStr){ + if(typeof it == 'object' && it){ + var value; + if(it instanceof Date){ // properly serialize dates + return '"' + dojo.date.stamp.toISOString(it,{zulu:true}) + '"'; + } + var id = it.__id; + if(id){ // we found an identifiable object, we will just serialize a reference to it... unless it is the root + if(path != '#' && ((useRefs && !id.match(/#/)) || paths[id])){ + var ref = id; + if(id.charAt(0)!='#'){ + if(it.__clientId == id){ + ref = "cid:" + id; + }else if(id.substring(0, idPrefix.length) == idPrefix){ // see if the reference is in the current context + // a reference with a prefix matching the current context, the prefix should be removed + ref = id.substring(idPrefix.length); + }else{ + // a reference to a different context, assume relative url based referencing + ref = id; + } + } + return serialize({ + $ref: ref + },'#'); + } + path = id; + }else{ + it.__id = path; // we will create path ids for other objects in case they are circular + generated[path] = it; + } + paths[path] = it;// save it here so they can be deleted at the end + _indentStr = _indentStr || ""; + var nextIndent = prettyPrint ? _indentStr + dojo.toJsonIndentStr : ""; + var newLine = prettyPrint ? "\n" : ""; + var sep = prettyPrint ? " " : ""; + + if(it instanceof Array){ + var res = dojo.map(it, function(obj,i){ + var val = serialize(obj, addProp(path, i), nextIndent); + if(typeof val != "string"){ + val = "undefined"; + } + return newLine + nextIndent + val; + }); + return "[" + res.join("," + sep) + newLine + _indentStr + "]"; + } + + var output = []; + for(var i in it){ + if(it.hasOwnProperty(i)){ + var keyStr; + if(typeof i == "number"){ + keyStr = '"' + i + '"'; + }else if(typeof i == "string" && (i.charAt(0) != '_' || i.charAt(1) != '_')){ + // we don't serialize our internal properties __id and __clientId + keyStr = dojo._escapeString(i); + }else{ + // skip non-string or number keys + continue; + } + var val = serialize(it[i],addProp(path, i),nextIndent); + if(typeof val != "string"){ + // skip non-serializable values + continue; + } + output.push(newLine + nextIndent + keyStr + ":" + sep + val); + } + } + return "{" + output.join("," + sep) + newLine + _indentStr + "}"; + }else if(typeof it == "function" && dojox.json.ref.serializeFunctions){ + return it.toString(); + } + + return dojo.toJson(it); // use the default serializer for primitives + } + var json = serialize(it,'#',''); + if(!indexSubObjects){ + for(var i in generated) {// cleanup the temporary path-generated ids + delete generated[i].__id; + } + } + return json; + }, + _addProp: function(id, prop){ + return id + (id.match(/#/) ? id.length == 1 ? '' : '.' : '#') + prop; + }, + _useRefs: false, + serializeFunctions: false +} diff --git a/schema/library/schema.js b/schema/library/schema.js new file mode 100644 index 0000000..1f661f9 --- /dev/null +++ b/schema/library/schema.js @@ -0,0 +1,218 @@ +dojo.provide("dojox.json.schema"); + + +dojox.json.schema.validate = function(/*Any*/instance,/*Object*/schema){ + // summary: + // To use the validator call this with an instance object and an optional schema object. + // If a schema is provided, it will be used to validate. If the instance object refers to a schema (self-validating), + // that schema will be used to validate and the schema parameter is not necessary (if both exist, + // both validations will occur). + // instance: + // The instance value/object to validate + // schema: + // The schema to use to validate + // description: + // The validate method will return an object with two properties: + // valid: A boolean indicating if the instance is valid by the schema + // errors: An array of validation errors. If there are no errors, then an + // empty list will be returned. A validation error will have two properties: + // property: which indicates which property had the error + // message: which indicates what the error was + // + return this._validate(instance,schema,false); +}; +dojox.json.schema.checkPropertyChange = function(/*Any*/value,/*Object*/schema){ + // summary: + // The checkPropertyChange method will check to see if an value can legally be in property with the given schema + // This is slightly different than the validate method in that it will fail if the schema is readonly and it will + // not check for self-validation, it is assumed that the passed in value is already internally valid. + // The checkPropertyChange method will return the same object type as validate, see JSONSchema.validate for + // information. + // value: + // The new instance value/object to check + // schema: + // The schema to use to validate + // return: + // see dojox.validate.jsonSchema.validate + // + return this._validate(value,schema,true); +}; +dojox.json.schema.mustBeValid = function(result){ + // summary: + // This checks to ensure that the result is valid and will throw an appropriate error message if it is not + // result: the result returned from checkPropertyChange or validate + if(!result.valid){ + throw new TypeError(dojo.map(result.errors,function(error){return error.property + ' ' + error.message;}).join(",")); + } +} +dojox.json.schema._validate = function(/*Any*/instance,/*Object*/schema,/*Boolean*/ _changing){ + + var errors = []; + // validate a value against a property definition + function checkProp(value, schema, path,i){ + var l; + path += path ? typeof i == 'number' ? '[' + i + ']' : typeof i == 'undefined' ? '' : '.' + i : i; + function addError(message){ + errors.push({property:path,message:message}); + } + + if((typeof schema != 'object' || schema instanceof Array) && (path || typeof schema != 'function')){ + if(typeof schema == 'function'){ + if(!(instance instanceof schema)){ + addError("is not an instance of the class/constructor " + schema.name); + } + }else if(schema){ + addError("Invalid schema/property definition " + schema); + } + return null; + } + if(_changing && schema.readonly){ + addError("is a readonly field, it can not be changed"); + } + if(schema['extends']){ // if it extends another schema, it must pass that schema as well + checkProp(value,schema['extends'],path,i); + } + // validate a value against a type definition + function checkType(type,value){ + if(type){ + if(typeof type == 'string' && type != 'any' && + (type == 'null' ? value !== null : typeof value != type) && + !(value instanceof Array && type == 'array') && + !(type == 'integer' && value%1===0)){ + return [{property:path,message:(typeof value) + " value found, but a " + type + " is required"}]; + } + if(type instanceof Array){ + var unionErrors=[]; + for(var j = 0; j < type.length; j++){ // a union type + if(!(unionErrors=checkType(type[j],value)).length){ + break; + } + } + if(unionErrors.length){ + return unionErrors; + } + }else if(typeof type == 'object'){ + var priorErrors = errors; + errors = []; + checkProp(value,type,path); + var theseErrors = errors; + errors = priorErrors; + return theseErrors; + } + } + return []; + } + if(value === undefined){ + if(!schema.optional){ + addError("is missing and it is not optional"); + } + }else{ + errors = errors.concat(checkType(schema.type,value)); + if(schema.disallow && !checkType(schema.disallow,value).length){ + addError(" disallowed value was matched"); + } + if(value !== null){ + if(value instanceof Array){ + if(schema.items){ + if(schema.items instanceof Array){ + for(i=0,l=value.length; i schema.maxItems){ + addError("There must be a maximum of " + schema.maxItems + " in the array"); + } + }else if(schema.properties){ + errors.concat(checkObj(value,schema.properties,path,schema.additionalProperties)); + } + if(schema.pattern && typeof value == 'string' && !value.match(schema.pattern)){ + addError("does not match the regex pattern " + schema.pattern); + } + if(schema.maxLength && typeof value == 'string' && value.length > schema.maxLength){ + addError("may only be " + schema.maxLength + " characters long"); + } + if(schema.minLength && typeof value == 'string' && value.length < schema.minLength){ + addError("must be at least " + schema.minLength + " characters long"); + } + if(typeof schema.minimum !== undefined && typeof value == typeof schema.minimum && + schema.minimum > value){ + addError("must have a minimum value of " + schema.minimum); + } + if(typeof schema.maximum !== undefined && typeof value == typeof schema.maximum && + schema.maximum < value){ + addError("must have a maximum value of " + schema.maximum); + } + if(schema['enum']){ + var enumer = schema['enum']; + l = enumer.length; + var found; + for(var j = 0; j < l; j++){ + if(enumer[j]===value){ + found=1; + break; + } + } + if(!found){ + addError("does not have a value in the enumeration " + enumer.join(", ")); + } + } + if(typeof schema.maxDecimal == 'number' && + (value.toString().match(new RegExp("\\.[0-9]{" + (schema.maxDecimal + 1) + ",}")))){ + addError("may only have " + schema.maxDecimal + " digits of decimal places"); + } + } + } + return null; + } + // validate an object against a schema + function checkObj(instance,objTypeDef,path,additionalProp){ + + if(typeof objTypeDef =='object'){ + if(typeof instance != 'object' || instance instanceof Array){ + errors.push({property:path,message:"an object is required"}); + } + + for(var i in objTypeDef){ + if(objTypeDef.hasOwnProperty(i) && "__id" != i){ + var value = instance[i]; + var propDef = objTypeDef[i]; + checkProp(value,propDef,path,i); + } + } + } + for(i in instance){ + if(instance.hasOwnProperty(i) && (i.charAt(0) != '_' || i.charAt(0) != '_') && objTypeDef && !objTypeDef[i] && additionalProp===false){ + errors.push({property:path,message:(typeof value) + "The property " + i + + " is not defined in the schema and the schema does not allow additional properties"}); + } + var requires = objTypeDef && objTypeDef[i] && objTypeDef[i].requires; + if(requires && !(requires in instance)){ + errors.push({property:path,message:"the presence of the property " + i + " requires that " + requires + " also be present"}); + } + value = instance[i]; + if(objTypeDef && typeof objTypeDef == 'object' && !(i in objTypeDef)){ + checkProp(value,additionalProp,path,i); + } + if(!_changing && value && value.$schema){ + errors = errors.concat(checkProp(value,value.$schema,path,i)); + } + } + return errors; + } + if(schema){ + checkProp(instance,schema,'',''); + } + if(!_changing && instance && instance.$schema){ + checkProp(instance,instance.$schema,'',''); + } + return {valid:!errors.length,errors:errors}; +}; + diff --git a/schema/media_link.json b/schema/media_link.json new file mode 100644 index 0000000..e249479 --- /dev/null +++ b/schema/media_link.json @@ -0,0 +1,27 @@ +{ + "type" : "object", + "title" : "media_link", + "description" : "Visual representation of an object in the form of an image, video or embedded HTML fragments", + "properties": { + "duration" : { + "title" : "duration", + "type" : "number", + "description" :"A hint to the consumer about the length, in seconds, of the media resource identified by the url property. A media link MAY contain a \"duration\" property when the target resource is a time-based media item such as an audio or video." + }, + "height" : { + "title" : "height", + "type" : "number", + "description" :"A hint to the consumer about the height, in pixels, of the media resource identified by the url property. A media link MAY contain a height property when the target resource is a visual media item such as an image, video or embeddable HTML page." + }, + "width" : { + "title" : "width", + "type" : "number", + "description" :"A hint to the consumer about the width, in pixels, of the media resource identified by the url property. A media link MAY contain a width property when the target resource is a visual media item such as an image, video or embeddable HTML page." + }, + "url" : { + "type": "string", + "required" : true, + "description" : "The IRI of the media resource being linked. A media link MUST have a url property." + } + } +} diff --git a/schema/object.json b/schema/object.json new file mode 100644 index 0000000..87c60b9 --- /dev/null +++ b/schema/object.json @@ -0,0 +1,77 @@ +{ + "type" : "object", + "title" : "object", + "description" : "Basic object on the web. The only required property is the id", + "properties" : { + "id" : { + "type" : "string", + "description" : "Provides a permanent, universally unique identifier for the object in the form of an absolute IRI [RFC3987]. An object SHOULD contain a single id property. If an object does not contain an id property, consumers MAY use the value of the url property as a less-reliable, non-unique identifier.", + "default" : "{link}" + }, + "image" : { + "format":"image", + "type":"object", + "properties" :{"$ref":"./media_link.json#properties"}, + "description" : "Description of a resource providing a visual representation of the object, intended for human consumption. An object MAY contain an image property whose value is a Media Link." + }, + "displayName" : { + "type":"string", + "description" : "A natural-language, human-readable and plain-text name for the object. HTML markup MUST NOT be included. An object MAY contain a displayName property. If the object does not specify an objectType property, the object SHOULD specify a displayName" + }, + "summary" : { + "type" : "string", + "description" : "Natural-language summary of the object encoded as a single JSON String containing HTML markup. Visual elements such as thumbnail images MAY be included. An activity MAY contain a summary property" + }, + "content" : { + "type" : "string", + "description" : "Natural-language description of the object encoded as a single JSON String containing HTML markup. Visual elements such as thumbnail images MAY be included. An object MAY contain a content property" + }, + "url" : { + "type" : "string", + "format" : "url", + "description" : "An IRI [RFC3987] identifying a resource providing an HTML representation of the object. An object MAY contain a url property" + }, + "objectType" :{ + "type" : "string", + "description" : "Identifies the type of object. An object MAY contain an objectType property whose value is a JSON String that is non-empty and matches either the \"isegment-nz-nc\" or the \"IRI\" production in [RFC3987]. Note that the use of a relative reference other than a simple name is not allowed. If no objectType property is contained, the object has no specific type." + }, + "author" : { + "type" : "object", + "description" : "Describes the entity that created or authored the object. An object MAY contain a single author property whose value is an Object of any type. Note that the author field identifies the entity that created the object and does not necessarily identify the entity that published the object. For instance, it may be the case that an object created by one person is posted and published to a system by an entirely different entity", + "properties" : {"$ref":"#properties"} + }, + "published" : { + "type" : "string", + "description": "[RFC3339] date-time. The date and time at which the object was published. An object MAY contain a published property", + "format" : "date-time" + }, + "updated" : { + "type" : "string", + "description": "[RFC3339] date-time. The date and time at which a previously published object has been modified. An Object MAY contain an updated property.", + "format" : "date-time" + }, + "attachments":{ + "title" : "Related objects", + "description" : "A collection of one or more additional, associated objects, similar to the concept of attached files in an email message. An object MAY have an attachedObjects property whose value is a JSON Array of Objects.", + "type" : "array", + "items": { + "type" : "object", + "properties" :{"$ref":"./core#properties"} + } + }, + "upstreamDuplicates":{ + "type" : "array", + "description" : "A JSON Array of one or more absolute IRI's [RFC3987] identifying objects that duplicate this object's content. An object SHOULD contain an upstreamDuplicates property when a publisher is knowingly duplicating with a new ID the content from another object. This MAY be used as a hint for consumers to use when resolving duplicates between objects received from different sources", + "items": {"type" : "string"} + }, + "downstreamDuplicates":{ + "type" : "array", + "description" : "A JSON Array of one or more absolute IRI's [RFC3987] identifying objects that duplicate this object's content. An object SHOULD contain a downstreamDuplicates property when there are known objects, possibly in a different system, that duplicate the content in this object. This MAY be used as a hint for consumers to use when resolving duplicates between objects received from different sources.", + "items": {"type" : "string"} + } + }, + "additionalProperties" : true, + "links": [ + {"rel": "describedby", "href": "./objectType/{objectType}.json"} + ] +} \ No newline at end of file diff --git a/schema/objectTypes/article.json b/schema/objectTypes/article.json new file mode 100644 index 0000000..64f8912 --- /dev/null +++ b/schema/objectTypes/article.json @@ -0,0 +1,11 @@ +{ + "type" : "object", + "title" : "article", + "extends": {"$ref":"http://activitystrea.ms/json-schema/object.json"}, + "properties" :{ + "objectType" :{ + "type" :"string", + "default" : "article" + } + } +} diff --git a/schema/objectTypes/audio.json b/schema/objectTypes/audio.json new file mode 100644 index 0000000..c773cc3 --- /dev/null +++ b/schema/objectTypes/audio.json @@ -0,0 +1,11 @@ +{ + "type" : "object", + "title" : "audio", + "extends": {"$ref":"http://activitystrea.ms/json-schema/object.json"}, + "properties" :{ + "objectType" :{ + "type" :"string", + "default" : "audio" + } + } +} \ No newline at end of file diff --git a/schema/objectTypes/bookmark.json b/schema/objectTypes/bookmark.json new file mode 100644 index 0000000..59f6a4d --- /dev/null +++ b/schema/objectTypes/bookmark.json @@ -0,0 +1,11 @@ +{ + "type" : "object", + "title" : "bookmark", + "extends": {"$ref":"http://activitystrea.ms/json-schema/object.json"}, + "properties" :{ + "objectType" :{ + "type" :"string", + "default" : "bookmark" + } + } +} \ No newline at end of file diff --git a/schema/objectTypes/comment.json b/schema/objectTypes/comment.json new file mode 100644 index 0000000..8adc41f --- /dev/null +++ b/schema/objectTypes/comment.json @@ -0,0 +1,11 @@ +{ + "type" : "object", + "title" : "comment", + "extends": {"$ref":"http://activitystrea.ms/json-schema/object.json"}, + "properties" :{ + "objectType" :{ + "type" :"string", + "default" : "comment" + } + } +} \ No newline at end of file diff --git a/schema/objectTypes/event.json b/schema/objectTypes/event.json new file mode 100644 index 0000000..a0cb7d5 --- /dev/null +++ b/schema/objectTypes/event.json @@ -0,0 +1,13 @@ +{ + "type" : "object", + "title" : "event", + "description" : "xCal fromat for vevent", + "extends": [{"$ref":"http://activitystrea.ms/json-schema/object.json"}, + {"$ref":"http://www.json-schema.org/calendar"}], + "properties" :{ + "objectType" :{ + "type" :"string", + "default" : "event" + } + } +} diff --git a/schema/objectTypes/file.json b/schema/objectTypes/file.json new file mode 100644 index 0000000..1c15e1d --- /dev/null +++ b/schema/objectTypes/file.json @@ -0,0 +1,11 @@ +{ + "type" : "object", + "title" : "file", + "extends": {"$ref":"http://activitystrea.ms/json-schema/object.json"}, + "properties" :{ + "objectType" :{ + "type" :"string", + "default" : "file" + } + } +} \ No newline at end of file diff --git a/schema/objectTypes/folder.json b/schema/objectTypes/folder.json new file mode 100644 index 0000000..0bd3ce6 --- /dev/null +++ b/schema/objectTypes/folder.json @@ -0,0 +1,11 @@ +{ + "type" : "object", + "title" : "folder", + "extends": {"$ref":"http://activitystrea.ms/json-schema/object.json"}, + "properties" :{ + "objectType" :{ + "type" :"string", + "default" : "folder" + } + } +} \ No newline at end of file diff --git a/schema/objectTypes/group.json b/schema/objectTypes/group.json new file mode 100644 index 0000000..b47d5e9 --- /dev/null +++ b/schema/objectTypes/group.json @@ -0,0 +1,11 @@ +{ + "type" : "object", + "title" : "group", + "extends": {"$ref":"http://activitystrea.ms/json-schema/object.json"}, + "properties" :{ + "objectType" :{ + "type" :"string", + "default" : "group" + } + } +} \ No newline at end of file diff --git a/schema/objectTypes/list.json b/schema/objectTypes/list.json new file mode 100644 index 0000000..18518e9 --- /dev/null +++ b/schema/objectTypes/list.json @@ -0,0 +1,18 @@ +{ + "type" : "object", + "title" : "list", + "extends": {"$ref":"http://activitystrea.ms/json-schema/object.json"}, + "properties" :{ + "objectType" :{ + "type" :"string", + "default" : "list" + }, + "items" : { + "type" : "array", + "items" : { + "type" : "object", + "properties" : {"$ref":"http://activitystrea.ms/json-schema/object.json"} + } + } + } +} diff --git a/schema/objectTypes/note.json b/schema/objectTypes/note.json new file mode 100644 index 0000000..8ecb91c --- /dev/null +++ b/schema/objectTypes/note.json @@ -0,0 +1,11 @@ +{ + "type" : "object", + "title" : "note", + "extends": {"$ref":"http://activitystrea.ms/json-schema/object.json"}, + "properties" :{ + "objectType" :{ + "type" :"string", + "default" : "note" + } + } +} diff --git a/schema/objectTypes/person.json b/schema/objectTypes/person.json new file mode 100644 index 0000000..7ffabff --- /dev/null +++ b/schema/objectTypes/person.json @@ -0,0 +1,13 @@ +{ + "type" : "object", + "title" : "person", + "description" : "vCard Format. Does not match PoCO", + "extends": [{"$ref":"http://activitystrea.ms/json-schema/object.json"}, + {"$ref":"http://www.json-schema.org/card"}], + "properties" :{ + "objectType" :{ + "type" :"string", + "default" : "person" + } + } +} diff --git a/schema/objectTypes/photo-album.json b/schema/objectTypes/photo-album.json new file mode 100644 index 0000000..76c7110 --- /dev/null +++ b/schema/objectTypes/photo-album.json @@ -0,0 +1,11 @@ +{ + "type" : "object", + "title" : "article", + "extends": {"$ref":"http://activitystrea.ms/json-schema/objectTypes/list.json"}, + "properties" :{ + "objectType" :{ + "type" :"string", + "default" : "photo-album" + } + } +} \ No newline at end of file diff --git a/schema/objectTypes/photo.json b/schema/objectTypes/photo.json new file mode 100644 index 0000000..f7fd732 --- /dev/null +++ b/schema/objectTypes/photo.json @@ -0,0 +1,15 @@ +{ + "type" : "object", + "title" : "photo", + "extends": {"$ref":"http://activitystrea.ms/json-schema/objectTypes/file.json"}, + "properties" :{ + "objectType" :{ + "type" :"string", + "default" : "photo" + }, + "displayName" : { + "type" : "string", + "default" : "a photo" + } + } +} diff --git a/schema/objectTypes/place.json b/schema/objectTypes/place.json new file mode 100644 index 0000000..7f20aea --- /dev/null +++ b/schema/objectTypes/place.json @@ -0,0 +1,11 @@ +{ + "type" : "object", + "title" : "place", + "extends": [{"$ref":"http://activitystrea.ms/json-schema/object.json"},{"$ref":"http://www.json-schema.org/address"}], + "properties" :{ + "objectType" :{ + "type" :"string", + "default" : "photo" + } + } +} \ No newline at end of file diff --git a/schema/objectTypes/playlist.json b/schema/objectTypes/playlist.json new file mode 100644 index 0000000..9f88983 --- /dev/null +++ b/schema/objectTypes/playlist.json @@ -0,0 +1,11 @@ +{ + "type" : "object", + "title" : "playlist", + "extends": {"$ref":"http://activitystrea.ms/json-schema/objectTypes/list.json"}, + "properties" :{ + "objectType" :{ + "type" :"string", + "default" : "playlist" + } + } +} \ No newline at end of file diff --git a/schema/objectTypes/product.json b/schema/objectTypes/product.json new file mode 100644 index 0000000..c7ad707 --- /dev/null +++ b/schema/objectTypes/product.json @@ -0,0 +1,11 @@ +{ + "type" : "object", + "title" : "product", + "extends": {"$ref":"http://activitystrea.ms/json-schema/object.json"}, + "properties" :{ + "objectType" :{ + "type" :"string", + "default" : "product" + } + } +} \ No newline at end of file diff --git a/schema/objectTypes/property.json b/schema/objectTypes/property.json new file mode 100644 index 0000000..447065f --- /dev/null +++ b/schema/objectTypes/property.json @@ -0,0 +1,42 @@ +{ + "type" : "object", + "title" : "property", + "description" : "A property describes name, path and value. Can be used with delete, update or post verbs", + "extends": {"$ref":"http://activitystrea.ms/json-schema/object.json"}, + "properties" :{ + "objectType" :{ + "type" :"string", + "default" : "property" + }, + "displayName" : { + "type" : "string", + "description" : "The human readable name of the property in the appropriate language", + "optional" : true + }, + "path" : { + "type" : "string", + "description" : "dot delimited path to the property in the target. Ex: streetAddress" + }, + "content" : { + "type" : "any", + "description" : "Value of the property it can be a scalar or an object" + } + }, + "example" : { + "actor" : {"id":1212, "displayName" : "Peter"}, + "verb" : "update", + "time" : "2010-08-02T15:29:00Z", + "object" : + { + "objectType" : "property", + "displayName" : "street address", + "path" : "streetAddress", + "content" : "234 Amazing St" + }, + "target" : { + "id": 12121, + "time" : "2010-08-02T15:29:00Z", + "displayName" : "Peter's House" + } + } +} \ No newline at end of file diff --git a/schema/objectTypes/review.json b/schema/objectTypes/review.json new file mode 100644 index 0000000..18945da --- /dev/null +++ b/schema/objectTypes/review.json @@ -0,0 +1,11 @@ +{ + "type" : "object", + "title" : "review", + "extends": {"$ref":"http://activitystrea.ms/json-schema/objectTypes/article.json"}, + "properties" :{ + "objectType" :{ + "type" :"string", + "default" : "review" + } + } +} \ No newline at end of file diff --git a/schema/objectTypes/service.json b/schema/objectTypes/service.json new file mode 100644 index 0000000..cce3bda --- /dev/null +++ b/schema/objectTypes/service.json @@ -0,0 +1,11 @@ +{ + "type" : "object", + "title" : "service", + "extends": {"$ref":"http://activitystrea.ms/json-schema/object.json"}, + "properties" :{ + "objectType" :{ + "type" :"string", + "default" : "service" + } + } +} \ No newline at end of file diff --git a/schema/objectTypes/song.json b/schema/objectTypes/song.json new file mode 100644 index 0000000..843fbbd --- /dev/null +++ b/schema/objectTypes/song.json @@ -0,0 +1,11 @@ +{ + "type" : "object", + "title" : "song", + "extends": {"$ref":"http://activitystrea.ms/json-schema/objectTypes/audio.json"}, + "properties" :{ + "objectType" :{ + "type" :"string", + "default" : "song" + } + } +} diff --git a/schema/objectTypes/status.json b/schema/objectTypes/status.json new file mode 100644 index 0000000..5074d35 --- /dev/null +++ b/schema/objectTypes/status.json @@ -0,0 +1,11 @@ +{ + "type" : "object", + "title" : "status", + "extends": {"$ref":"http://activitystrea.ms/json-schema/objectTypes/note.json"}, + "properties" :{ + "objectType" :{ + "type" :"string", + "default" : "song" + } + } +} \ No newline at end of file diff --git a/schema/objectTypes/video.json b/schema/objectTypes/video.json new file mode 100644 index 0000000..d827a4f --- /dev/null +++ b/schema/objectTypes/video.json @@ -0,0 +1,11 @@ +{ + "type" : "object", + "title" : "video", + "extends": {"$ref":"http://activitystrea.ms/json-schema/objectTypes/file.json"}, + "properties" :{ + "objectType" :{ + "type" :"string", + "default" : "video" + } + } +} diff --git a/schema/verbs/favorite.json b/schema/verbs/favorite.json new file mode 100644 index 0000000..8f0e44d --- /dev/null +++ b/schema/verbs/favorite.json @@ -0,0 +1,16 @@ +{ + "type" : "object", + "title" : "Favorite", + "description" : "To mark an object as a favorite", + "extends": {"$ref":"http://activitystrea.ms/json-schema/activity.json"}, + "properties" :{ + "verb" :{ + "type" : "string", + "default" : "favorite" + }, + "title" : { + "type" : "string", + "default" : "{actor.displayName} marked {object.displayName} as a favorite" + } + } +} diff --git a/schema/verbs/follow.json b/schema/verbs/follow.json new file mode 100644 index 0000000..9293fc7 --- /dev/null +++ b/schema/verbs/follow.json @@ -0,0 +1,16 @@ +{ + "type" : "object", + "title" : "Follow", + "description" : "To start following an object", + "extends": {"$ref":"http://activitystrea.ms/json-schema/activity.json"}, + "properties" :{ + "verb" :{ + "type" : "string", + "default" : "follow" + }, + "title" : { + "type" : "string", + "default" : "{actor.displayName} started following {object.displayName}" + } + } +} diff --git a/schema/verbs/join.json b/schema/verbs/join.json new file mode 100644 index 0000000..04e7fe3 --- /dev/null +++ b/schema/verbs/join.json @@ -0,0 +1,16 @@ +{ + "type" : "object", + "title" : "Join", + "description" : "To join an object. Usually a group", + "extends": {"$ref":"http://activitystrea.ms/json-schema/activity.json"}, + "properties" :{ + "verb" :{ + "type" : "string", + "default" : "join" + }, + "title" : { + "type" : "string", + "default" : "{actor.displayName} joined {object.displayName}" + } + } +} diff --git a/schema/verbs/like.json b/schema/verbs/like.json new file mode 100644 index 0000000..30a921a --- /dev/null +++ b/schema/verbs/like.json @@ -0,0 +1,16 @@ +{ + "type" : "object", + "title" : "Like", + "description" : "To like an object", + "extends": {"$ref":"http://activitystrea.ms/json-schema/activity.json"}, + "properties" :{ + "verb" :{ + "type" : "string", + "default" : "like" + }, + "title" : { + "type" : "string", + "default" : "{actor.displayName} likes {object.displayName}" + } + } +} diff --git a/schema/verbs/make-friend.json b/schema/verbs/make-friend.json new file mode 100644 index 0000000..748a7bc --- /dev/null +++ b/schema/verbs/make-friend.json @@ -0,0 +1,16 @@ +{ + "type" : "object", + "title" : "Befriend", + "description" : "To friend an object. Usually a person", + "extends": {"$ref":"http://activitystrea.ms/json-schema/activity.json"}, + "properties" :{ + "verb" :{ + "type" : "string", + "default" : "make-friend" + }, + "title" : { + "type" : "string", + "default" : "{actor.displayName} is now friends with {object.displayName}" + } + } +} diff --git a/schema/verbs/play.json b/schema/verbs/play.json new file mode 100644 index 0000000..50dcf90 --- /dev/null +++ b/schema/verbs/play.json @@ -0,0 +1,16 @@ +{ + "type" : "object", + "title" : "Play", + "description" : "To play an object", + "extends": {"$ref":"http://activitystrea.ms/json-schema/activity.json"}, + "properties" :{ + "verb" :{ + "type" : "string", + "default" : "play" + }, + "title" : { + "type" : "string", + "default" : "{actor.displayName} played {object.displayName}" + } + } +} diff --git a/schema/verbs/post.json b/schema/verbs/post.json new file mode 100644 index 0000000..921d865 --- /dev/null +++ b/schema/verbs/post.json @@ -0,0 +1,16 @@ +{ + "type" : "object", + "title" : "Post", + "description" : "To publish an object", + "extends": {"$ref":"http://activitystrea.ms/json-schema/activity.json"}, + "properties" :{ + "verb" :{ + "type" : "string", + "default" : "post" + }, + "title" : { + "type" : "string", + "default" : "{actor.displayName} posted {object.displayName}" + } + } +} diff --git a/schema/verbs/rsvp-maybe.json b/schema/verbs/rsvp-maybe.json new file mode 100644 index 0000000..d6e77db --- /dev/null +++ b/schema/verbs/rsvp-maybe.json @@ -0,0 +1,16 @@ +{ + "type" : "object", + "title" : "Rsvp Maybe", + "description" : "To indicate that the actor may attend an event", + "extends": {"$ref":"http://activitystrea.ms/json-schema/activity.json"}, + "properties" :{ + "verb" :{ + "type" : "string", + "default" : "rsvp-maybe" + }, + "title" : { + "type" : "string", + "default" : "{actor.displayName} may be attending {object.displayName}" + } + } +} diff --git a/schema/verbs/rsvp-no.json b/schema/verbs/rsvp-no.json new file mode 100644 index 0000000..453aa57 --- /dev/null +++ b/schema/verbs/rsvp-no.json @@ -0,0 +1,16 @@ +{ + "type" : "object", + "title" : "Rsvp No", + "description" : "To indicate that the actor will not attend an event", + "extends": {"$ref":"http://activitystrea.ms/json-schema/activity.json"}, + "properties" :{ + "verb" :{ + "type" : "string", + "default" : "rsvp-no" + }, + "title" : { + "type" : "string", + "default" : "{actor.displayName} will not attend {object.displayName}" + } + } +} diff --git a/schema/verbs/rsvp-yes.json b/schema/verbs/rsvp-yes.json new file mode 100644 index 0000000..73bfb38 --- /dev/null +++ b/schema/verbs/rsvp-yes.json @@ -0,0 +1,16 @@ +{ + "type" : "object", + "title" : "Rsvp Yes", + "description" : "To indicate that the actor will attend an event", + "extends": {"$ref":"http://activitystrea.ms/json-schema/activity.json"}, + "properties" :{ + "verb" :{ + "type" : "string", + "default" : "rsvp-yes" + }, + "title" : { + "type" : "string", + "default" : "{actor.displayName} is attending {object.displayName}" + } + } +} diff --git a/schema/verbs/save.json b/schema/verbs/save.json new file mode 100644 index 0000000..6917c14 --- /dev/null +++ b/schema/verbs/save.json @@ -0,0 +1,16 @@ +{ + "type" : "object", + "title" : "Save", + "description" : "To save an object", + "extends": {"$ref":"http://activitystrea.ms/json-schema/activity.json"}, + "properties" :{ + "verb" :{ + "type" : "string", + "default" : "save" + }, + "title" : { + "type" : "string", + "default" : "{actor.displayName} saved {object.displayName}" + } + } +} diff --git a/schema/verbs/share.json b/schema/verbs/share.json new file mode 100644 index 0000000..934fd3a --- /dev/null +++ b/schema/verbs/share.json @@ -0,0 +1,16 @@ +{ + "type" : "object", + "title" : "Share", + "description" : "To share an object.", + "extends": {"$ref":"http://activitystrea.ms/json-schema/activity.json"}, + "properties" :{ + "verb" :{ + "type" : "string", + "default" : "share" + }, + "title" : { + "type" : "string", + "default" : "{actor.displayName} shared {object.displayName}" + } + } +} diff --git a/schema/verbs/tag.json b/schema/verbs/tag.json new file mode 100644 index 0000000..5d9a6b3 --- /dev/null +++ b/schema/verbs/tag.json @@ -0,0 +1,16 @@ +{ + "type" : "object", + "title" : "Tag", + "description" : "To tag an object. Ex: A person being tagged in a photo", + "extends": {"$ref":"http://activitystrea.ms/json-schema/activity.json"}, + "properties" :{ + "verb" :{ + "type" : "string", + "default" : "tag" + }, + "title" : { + "type" : "string", + "default" : "{actor.displayName} tagged {object.displayName} in {target.displayName}" + } + } +} diff --git a/schema/verbs/update-multiple.json b/schema/verbs/update-multiple.json new file mode 100644 index 0000000..3b0060b --- /dev/null +++ b/schema/verbs/update-multiple.json @@ -0,0 +1,48 @@ +{ + "type" : "object", + "title" : "Update Multiple", + "notes" : "Warning:Reviewing this concept. This is what RT team wants", + "description" : "To update multiple things on an object. The object will either be a changeset or a list", + "extends": {"$ref":"http://activitystrea.ms/json-schema/activity.json"}, + "properties" :{ + "verb" :{ + "type" : "string", + "default" : "update-multiple" + }, + "object" : { + "type" : "object", + "title" : "changeset", + "properties" : { + "actions" : { + "type" : "array", + "items" : { + "type" : "object", + "properties" : {"$ref":"http://activitystrea.ms/json-schema/action.json"} + } + } + } + } + }, + "example" : { + "actor" : {"id":1212, "displayName" : "Melissa"}, /*May not be known for FB */ + "verb" : "update-multiple", /* ignore for FB */ + "time" : "2010-08-02T15:29:00Z", + "target" : { /* uid --> id on FB */ + "type" : "profile", + "id" : "2121212", + "displayName" : "Melissa's profile", + "time" : "2009-08-02T15:29:00Z" + }, + "object" : { /* data */ + "type" : "changeset", + "actions" : [ + {"verb" : "update", "object" : {"type" : "property", "displayName" : "relationship status"}}, + {"verb" : "delete", "object" : {"type" : "song", "displayName" : "99 Balloons"}}, + {"verb" : "post", "object" : {"type": "song", "displayName" : "Ever ever after"}} + ] + } + } + } + + } +} diff --git a/schema/verbs/update.json b/schema/verbs/update.json new file mode 100644 index 0000000..c9e43ec --- /dev/null +++ b/schema/verbs/update.json @@ -0,0 +1,26 @@ +{ + "type" : "object", + "title" : "Update", + "description" : "To update a property on an object. Ex: Monica updated her street address to 234 Awesome St.", + "notes" : "The new property value can be a scalar value or an object. See the property object type", + "extends": {"$ref":"http://activitystrea.ms/json-schema/activity.json"}, + "properties" :{ + "verb" :{ + "type" : "string", + "default" : "update" + }, + "title" : { + "type" : "string", + "default" : "{actor.displayName} updated {target.displayName}'s {object.displayName} to {object.value}" + }, + "object" : { + "type" : "object", + "properties" : {"$ref":"http://activitystrea.ms/json-schema/objectTypes/property.json"} + }, + "target" : { + "type" : "object", + "optional" : true, + "properties" : {"$ref":"http://activitystrea.ms/json-schema/core-object.json"} + } + } +}