├── README.md
├── api
├── application
│ ├── hal.php
│ ├── hal
│ │ ├── joomla.php
│ │ └── link.php
│ ├── includemap.php
│ ├── resourcemap.php
│ ├── router.php
│ └── web.php
├── controller
│ ├── base.php
│ ├── controller.php
│ ├── item.php
│ └── list.php
├── database
│ └── query.php
├── document
│ └── hal
│ │ └── json.php
├── import.php
├── index.php
├── services
│ ├── categories
│ │ ├── get.php
│ │ ├── list
│ │ │ ├── embedded.json
│ │ │ └── get.php
│ │ └── resource.json
│ ├── menuitems
│ │ ├── get.php
│ │ ├── list
│ │ │ ├── embedded.json
│ │ │ └── get.php
│ │ └── resource.json
│ └── root
│ │ └── get.php
└── transform
│ ├── base.php
│ ├── boolean.php
│ ├── datetime.php
│ ├── float.php
│ ├── int.php
│ ├── position.php
│ ├── state.php
│ ├── string.php
│ ├── target.php
│ ├── transform.php
│ └── ynglobal.php
├── components
├── com_content
│ ├── services.json
│ └── services
│ │ ├── articles
│ │ ├── application.php
│ │ ├── get.php
│ │ ├── list
│ │ │ ├── embedded.json
│ │ │ └── get.php
│ │ ├── resource.json
│ │ └── transform
│ │ │ └── position.php
│ │ └── index.html
└── com_weblinks
│ ├── services.json
│ └── services
│ ├── index.html
│ └── weblinks
│ ├── get.php
│ ├── list
│ ├── create.php
│ ├── embedded.json
│ └── get.php
│ └── resource.json
└── etc
├── README.md
├── config.json
└── services.json
/README.md:
--------------------------------------------------------------------------------
1 | j3-rest-api
2 | ===========
3 | This is an experimental REST API for Joomla 3.x and these are some rough notes to
4 | get you started. DO NOT PUT THIS ON A PRODUCTION SITE. This is proof-of-concept
5 | code that is far from being complete or stable. There is no access level security
6 | (yet) so any content in your website will be publicly exposed by this code.
7 |
8 | ## Introduction
9 | This proof-of-concept demonstrates how a web services API might be created for
10 | Joomla 3.x. It runs as a standalone application which merges services that have
11 | been added to otherwise completely unmodified components. No core code is changed.
12 |
13 | The web services code makes use of the RESTful router included in the
14 | Joomla Platform code in Joomla 3.x.
15 |
16 | Any and all feedback is welcome, particularly at this stage regarding the
17 | architecture. Please send your comments to chris.davenport@joomla.org.
18 | Pull requests also welcome of course. Thanks!
19 |
20 | ## Prerequisites
21 | You must have an already installed and working installation of Joomla 3.x.
22 |
23 | ## Installation
24 | Grab the code from GitHub: https://github.com/chrisdavenport/j3-rest-api and put
25 | it in your Joomla web root.
26 |
27 | Add the following lines to your .htaccess file:
28 |
29 | ```
30 | # If the requested path and file is not /index.php and the request
31 | # has not already been internally rewritten to the index.php script
32 | RewriteCond %{REQUEST_URI} !^/index\.php
33 | # and the request is for something within the api folder
34 | RewriteCond %{REQUEST_URI} /api/ [NC]
35 | # and the requested path and file doesn't directly match a physical file
36 | RewriteCond %{REQUEST_FILENAME} !-f
37 | # and the requested path and file doesn't directly match a physical folder
38 | RewriteCond %{REQUEST_FILENAME} !-d
39 | # internally rewrite the request to the API index.php script
40 | RewriteRule .* api/index.php [L]
41 | ```
42 |
43 | Point a web browser at [path-to-Joomla]/api
44 |
45 | You should get some JSON back. This is the entry point service document described
46 | below.
47 |
48 | ## Using the HAL Browser
49 | You can browse the API interactively using Mike Kelly's HAL Browser. As the /api/hal-browser
50 | directory in this repository is corrupted in some weird way I haven't been able to fix, you
51 | will need to download and install it yourself from https://github.com/mikekelly/hal-browser.
52 |
53 | Once installed simply point a web browser at the following URL (adjusted for your environment):
54 |
55 | ```
56 | http://www.example.com/path-to-Joomla/api/hal-browser/browser.html#http://www.example.com/path-to-Joomla/api
57 | ```
58 | Important: You will need to add the following line to your /etc/config.json file
59 | in order for the HAL Browser to work:
60 |
61 | ```
62 | "absoluteHrefs": true
63 | ```
64 |
65 | ## Quick tour of the code
66 | The “core” code lives in the new /api directory, which is the entry point for the
67 | API application.
68 |
69 | Basic configuration files go in /etc although the standard Joomla configuration.php
70 | file is also loaded in order to get the database credentials.
71 |
72 | A new /services directory will have been added to each component for which support
73 | has been provided. The currently supported components are: articles/content,
74 | categories and weblinks. They demonstrate how an extension can have web services
75 | code added to it. The current code does not make use of any component code,
76 | and installing it does not overwrite any current code, but sharing model code at
77 | least is something that should be seriously considered.
78 |
79 | In the distributed code the Content-Type headers returned should be correct as per
80 | the specification (eg. application/vnd.joomla.service.v1+hal+json”). The HAL Browser
81 | will work with these just fine, however if you use a web browser to access the
82 | API directly they will not normally be recognised. If you want to test the API
83 | in a web browser without using the HAL Browser, then comment out the setMimeEncoding
84 | line in the execute method in the /api/controller/base.php file. The API will
85 | then return the default Content-Type: application/json header.
86 |
87 | ## Entry point service document
88 | Pointing a web browser at the api entry point: http://www.example.com/api will return the “service” document which lists the services
89 | available via the API. The format of this document is described in "application/vnd.joomla.service.v1 media type specification" referenced below.
90 | The code responsible for handling this document can be found in
91 |
92 | ```
93 | /api/services/root/get.php
94 | ```
95 |
96 | ## Adding web services support to a component
97 | Look in /components/com_content and /components/com_weblinks for examples of how this could be done. The core web services code looks for a services.json file in each of the installed component directories. If it finds one it automatically merges it into the services router map.
98 |
99 | Here’s the router map for com_content:
100 |
101 | ```javascript
102 | {
103 | "joomla:articles":"component/content/ArticlesList",
104 | "joomla:articles/:id":"component/content/Articles"
105 | "joomla:categories/:catid/articles":"component/content/ArticlesList"
106 | }
107 | ```
108 |
109 | Each line consists of a “public” route and the associated “internal” route. So on the first line we have “joomla:articles” mapping to "component/content/ArticlesList", which means that a GET request to this URL:
110 |
111 | ```
112 | http://www.example.com/api/joomla:articles
113 | ```
114 |
115 | will cause the controller class ComponentContentArticlesListGet to be loaded from the file
116 |
117 | ```
118 | /components/com_content/services/articles/list/get.php
119 | ```
120 |
121 | The JLoader prefix “ComponentContentArticles” is automatically set up to point to the correct directory, so provided you have the correct class in the correct file the PHP loader will find it without further effort.
122 |
123 | The document returned in this case will contain a (paginated) list of articles and is described in "application/vnd.joomla.list.v1 media type specification" referenced below.
124 |
125 | The second line of the services.json file maps URLs such as
126 |
127 | ```
128 | http://www.example.com/api/joomla:articles/1234
129 | ```
130 |
131 | to the controller class ComponentContentArticlesGet in the file
132 |
133 | ```
134 | /components/com_content/services/articles/get.php
135 | ```
136 |
137 | The document returned in this case will contain a representation of the single article requested (by its id) and is described in "application/vnd.joomla.item.v1 media type specification" referenced below.
138 |
139 | The fields returned for the articles resource are described in https://docs.google.com/a/joomla.org/document/d/1d5qQ16r1Bo1BlXXuyS_eFB4BQcfuSg05pn9hsMpAgqk/edit#heading=h.ygla5naoxuzt
140 |
141 | The third line of the services.json file maps URLs such as
142 |
143 | ```
144 | http://www.example.com/api/joomla:categories/383/articles
145 | ```
146 |
147 | to the same controller as the first line. In this instance it will return a list of all articles which are in category 383.
148 |
149 | ## Resource - database field mapping
150 | In order to establish the mapping between the database fields and the fields exposed in the API, a JSON object describes the relationship so that in many (most?) cases, adding web services support to an existing component is mostly a matter of creating these map files.
151 |
152 | The map is located in a resource.json file. So for com_content, for example, you should look in the file
153 |
154 | ```
155 | /components/com_content/services/articles/resource.json
156 | ```
157 |
158 | The JSON object contained in this file is a simple list of key-value pairs. All the fields represented by the key-value pairs will be included in a full representation of the resource. Both keys and values are strings which have a specific format that describes the detailed mapping between the field in the API representation (the key) and the field in the model (database) representation (the value).
159 |
160 | ### Key format
161 | The ABNF (RFC5234) syntax for the key is as follows:
162 |
163 | ```
164 | [ objectName ] “/” fieldName [ “.” propertyName ]
165 | ```
166 |
167 | where
168 |
169 |
170 |
171 | objectName |
172 | is the optional name of an object within the resource. The default object name is “main” and refers to the resource itself. |
173 |
174 |
175 | fieldName |
176 | is the name of the field that will be assigned the value. |
177 |
178 |
179 | propertyName |
180 | is used where fieldName is an object and propertyName refers to a property of that object. |
181 |
182 |
183 |
184 | ### Value format
185 | The ABNF (RFC5234) syntax for the value string is as follows:
186 |
187 | ```
188 | transformName “:” definition
189 | ```
190 |
191 | where
192 |
193 |
194 |
195 | transformName |
196 | is the name of a transform that will modify the value before passing it
197 | to the API object. This is often just a matter of type casting
198 | (eg. “string” or “int” transforms) but more sophisticated transforms
199 | are available (eg. “state”) and you can add your own or override the
200 | standard ones. See below for more information. |
201 |
202 |
203 | definition |
204 | is a string which is first parsed for model field names that will be substituted by their values before the resulting
205 | string is passed to the transform method. The definition string may contain the names of model fields enclosed in curly brackets.
206 | These will be automatically replaced by the model values. The field name may contain a “.” in which case the part before
207 | the dot refers to a JSON-encoded model field and the part after the dot refers to a field within that JSON-encoded data.
208 | The unpacking of JSON-encoded fields is handled automatically. |
209 |
210 |
211 |
212 | Some examples:
213 |
214 |
215 |
216 | string:{title} |
217 | Retrieves the title field from the model and casts it to a string. |
218 |
219 |
220 | string:post |
221 | Returns the literal string “post” (without the quotes). |
222 |
223 |
224 | string:/joomla:articles/{id} |
225 | Retrieves the id field from the model and substitutes it into the definition string. For example, if id has the value 987 then this will return the string “/joomla:articles/987”. |
226 |
227 |
228 |
229 | The standard transforms are defined in the /api/transform directory and are
230 | described here:
231 |
232 |
233 |
234 | int |
235 | Casts the definition string to an integer. |
236 |
237 |
238 | string |
239 | Casts the definition string to a string (duh!). |
240 |
241 |
242 | boolean |
243 | Casts the definition string to a boolean. |
244 |
245 |
246 | datetime |
247 | Returns an ISO 8601 date/time field [NOT IMPLEMENTED YET] |
248 |
249 |
250 | float |
251 | Returns “left”, “right”, “none” or “global”. |
252 |
253 |
254 | state |
255 | Returns “unpublished”, “published”, “trashed” or “archived”. |
256 |
257 |
258 | target |
259 | Returns “parent”, “new”, “popup”, “modal” or “global”. |
260 |
261 |
262 | ynglobal |
263 | Returns “yes”, “no” or “global”. |
264 |
265 |
266 |
267 | ## Adding a custom transform
268 | Components may add custom transforms override the standard ones if required.
269 | Create a /transform directory at the same level in the component directory structure
270 | as the resource.json file and add the additional or override transform classes
271 | in it. There is an example of this in
272 |
273 | ```
274 | /components/com_content/services/transform/position.php
275 | ```
276 |
277 | ## Defining which fields to embed in a list representation
278 | Because the list representation would not normally include a full representation of each of the embeddeded objects,
279 | there is a simple JSON file that defines which fields are included. The file for com_content is
280 |
281 | ```
282 | /components/com_content/services/articles/list/embedded.json
283 | ```
284 |
285 | and it contains something like this:
286 |
287 | ```javascript
288 | {
289 | "embedded":
290 | [
291 | "/access",
292 | "/featured",
293 | "/introText",
294 | "/language",
295 | "_links/self.href",
296 | "_links/joomla:assets.href",
297 | "_links/joomla:categories.href",
298 | "_links/joomla:checkout.href",
299 | "_links/joomla:checkout.method",
300 | "_links/joomla:introImage.href",
301 | "_links/joomla:introImage.float",
302 | "_links/joomla:introImage.title",
303 | "_links/joomla:introImage.caption",
304 | "metadata/authorName",
305 | "/ordering",
306 | "publish/alias",
307 | "publish/created",
308 | "publish/publishDown",
309 | "publish/publishUp",
310 | "/state",
311 | "/title"
312 | ]
313 | }
314 | ```
315 |
316 | The array is a simple list of field names to be included in the embedded representations.
317 | Each entry must match a field name in the articles.json file. The fields definitions from the resource.json file
318 | used so that data in both single and list representations should match exactly.
319 |
320 | ## References and further reading
321 |
322 | * Joomla Web Services Working Group http://docs.joomla.org/Web_Services_Working_Group
323 | * Joomla CMS Web Services API Specification https://docs.google.com/document/d/1FVKGlV6BN6pu-YH2WR2pQHE3Ez7M6r7LD417GSw9ZSo/edit?usp=sharing
324 | * application/vnd.joomla.base.v1 media type specification https://docs.google.com/document/d/11SqH-daKQV9SrFBMEpopjBk3vM1USIHnFWZB9rjJB94/edit?usp=sharing
325 | * application/vnd.joomla.service.v1 media type specification https://docs.google.com/document/d/1wg3AcgStA26UwDcbHVV1bub4sa_BhsKfzAmX21eG-FM/edit?usp=sharing
326 | * application/vnd.joomla.item.v1 media type specification https://docs.google.com/document/d/16xwxSDDPW0U1CG9l7JcwOyGvyjm7wv5zOSd9JwgF2iQ/edit?usp=sharing
327 | * application/vnd.joomla.list.v1 media type specification https://docs.google.com/document/d/1PLym28MG5v1tWyvIyW-9483JNKh5AP21Fmsmg62plnA/edit?usp=sharing
328 | * Joomla CMS Web Service API Implementation https://docs.google.com/document/d/1d5qQ16r1Bo1BlXXuyS_eFB4BQcfuSg05pn9hsMpAgqk/edit?usp=sharing
329 | * Joomla CMS CLI Services API Specification https://docs.google.com/document/d/1wI3cSm3y4aa8n8rojJKpiF6RUpSl63WFuLgJj2WqW8o/edit?usp=sharing
330 |
--------------------------------------------------------------------------------
/api/application/hal.php:
--------------------------------------------------------------------------------
1 | properties['_links']))
32 | {
33 | $this->properties['_links'] = new stdClass;
34 | }
35 |
36 | // Get the link relation name.
37 | $rel = $link->getRel();
38 |
39 | // Add the link to the array of links for the given link relation name.
40 | if (isset($this->properties['_links']->$rel))
41 | {
42 | $this->properties['_links']->$rel = array_merge($this->properties['_links']->$rel, array($link));
43 | }
44 | else
45 | {
46 | $this->properties['_links']->$rel = array($link);
47 | }
48 |
49 | return $this;
50 | }
51 |
52 | /**
53 | * Embed objects.
54 | *
55 | * @param string $name Name (rel) of embedded objects.
56 | * @param array $data Array of objects to be embedded.
57 | *
58 | * @return object This method may be chained.
59 | */
60 | public function embed($name, $data)
61 | {
62 | // If there is no _embedded property, create it.
63 | if (!isset($this->properties['_embedded']))
64 | {
65 | $this->properties['_embedded'] = new stdClass;
66 | }
67 |
68 | $this->properties['_embedded']->$name = $data;
69 |
70 | return $this;
71 | }
72 |
73 | /**
74 | * Method to get the value of a property.
75 | *
76 | * @param string $name Name of the property.
77 | * @param mixed $default Value returned if property does not exist.
78 | *
79 | * @return mixed Value of the property.
80 | */
81 | public function get($name, $default = null)
82 | {
83 | return isset($this->properties[$name]) ? $this->properties[$name] : $default;
84 | }
85 |
86 | /**
87 | * Method to return an object suitable for serialisation.
88 | *
89 | * @return stdClass A HAL object suitable for serialisation.
90 | */
91 | public function getHal()
92 | {
93 | $hal = new stdClass;
94 |
95 | // Links with the same link relation name are stored in an array, but if
96 | // the array contains only one item, then we drop the array.
97 | if (isset($this->properties['_links']) && !empty($this->properties['_links']))
98 | {
99 | // Get the links object.
100 | $links = $this->properties['_links'];
101 |
102 | // Convert single entry arrays to direct links.
103 | foreach ($links as $rel => $link)
104 | {
105 | if (is_array($link))
106 | {
107 | if (count($link) == 1)
108 | {
109 | $links->$rel = $link[0];
110 | }
111 | else
112 | {
113 | $links->$rel = $this->links[$rel];
114 | }
115 | }
116 | }
117 |
118 | // Replace the links object.
119 | $this->properties['_links'] = $links;
120 | }
121 |
122 | // Copy all the properties into the HAL object.
123 | if (!empty($this->properties))
124 | {
125 | foreach ($this->properties as $key => $value)
126 | {
127 | $hal->$key = $value;
128 | }
129 | }
130 |
131 | return $hal;
132 | }
133 |
134 | /**
135 | * Method to load an object or an array into this HAL object.
136 | *
137 | * @param object $object Object whose properties are to be loaded.
138 | *
139 | * @return object This method may be chained.
140 | */
141 | public function load($object)
142 | {
143 | foreach ($object as $name => $value)
144 | {
145 | // For _links and _embedded, we merge rather than replace.
146 | if ($name == '_links' || $name == '_embedded')
147 | {
148 | $this->properties[$name] = (object) array_merge((array) $this->properties[$name], (array) $value);
149 | }
150 | else
151 | {
152 | $this->properties[$name] = $value;
153 | }
154 | }
155 |
156 | return $this;
157 | }
158 |
159 | /**
160 | * Method to set the value of a property.
161 | *
162 | * @param string $name Name of the property.
163 | * @param mixed $value Value of the property.
164 | *
165 | * @return object This method may be chained.
166 | */
167 | public function set($name, $value)
168 | {
169 | $this->properties[$name] = $value;
170 |
171 | return $this;
172 | }
173 |
174 | }
175 |
--------------------------------------------------------------------------------
/api/application/hal/joomla.php:
--------------------------------------------------------------------------------
1 | meta = new stdClass;
55 | $this->meta->apiVersion = '1.0';
56 | $this->set('_meta', $this->meta);
57 |
58 | // Add standard Joomla namespace as curie.
59 | $joomlaCurie = new ApiApplicationHalLink('curies', 'http://docs.joomla.org/Link_relations/{rel}');
60 | $joomlaCurie->name = 'joomla';
61 | $joomlaCurie->templated = true;
62 | $this->addLink($joomlaCurie);
63 |
64 | // Add basic hypermedia links.
65 | $this->addLink(new ApiApplicationHalLink('base', rtrim(JUri::base(), '/')));
66 | if (isset($options['self']))
67 | {
68 | $this->addLink(new ApiApplicationHalLink('self', $options['self']));
69 | }
70 |
71 | // Set the content type.
72 | if (isset($options['contentType']))
73 | {
74 | $this->setMetadata('contentType', $options['contentType']);
75 | }
76 |
77 | // Set link to (human-readable) schema documentation.
78 | if (isset($options['describedBy']))
79 | {
80 | $this->setMetadata('describedBy', $options['describedBy']);
81 | }
82 |
83 | // Load the resource field map (if there is one).
84 | $resourceMapFile = isset($options['resourceMap']) ? $options['resourceMap'] : '';
85 | if ($resourceMapFile != '' && file_exists($resourceMapFile))
86 | {
87 | $basePath = dirname($options['resourceMap']);
88 | $this->resourceMap = new ApiApplicationResourcemap(array('basePath' => $basePath));
89 | $this->resourceMap->fromJson(file_get_contents($resourceMapFile));
90 | }
91 |
92 | // Load the embedded field map (if there is one).
93 | $embeddedMapFile = isset($options['embeddedMap']) ? $options['embeddedMap'] : '';
94 | if ($embeddedMapFile != '' && file_exists($embeddedMapFile))
95 | {
96 | // Load the embedded fields list.
97 | $this->includeMap = new ApiApplicationIncludemap();
98 | $this->includeMap->fromJson(file_get_contents($embeddedMapFile));
99 | }
100 | }
101 |
102 | /**
103 | * Import data into HAL object.
104 | *
105 | * @param string $name Name (rel) of data to be imported.
106 | * @param array $data Array of data items.
107 | *
108 | * @return object This object may be chained.
109 | */
110 | public function embed($name, $data)
111 | {
112 | // If there is no map then use the standard embed method.
113 | if (!($this->includeMap instanceof ApiApplicationIncludemap))
114 | {
115 | return parent::embed($name, $data);
116 | }
117 |
118 | // Get list of fields to be included.
119 | $include = $this->includeMap->toArray();
120 |
121 | // Transform the source data array.
122 | $resources = array();
123 | foreach ($data as $key => $datum)
124 | {
125 | $resources[$key] = $this->resourceMap->toExternal($datum, $include);
126 | }
127 |
128 | // Embed data into HAL object.
129 | parent::embed($name, $resources);
130 |
131 | // Add pagination URI template (per RFC6570).
132 | $pagesLink = new ApiApplicationHalLink('pages', '/' . $name . '{?fields,offset,page,perPage,sort}');
133 | $pagesLink->templated = true;
134 | $this->addLink($pagesLink);
135 |
136 | return $this;
137 | }
138 |
139 | /**
140 | * Method to return an object suitable for serialisation.
141 | *
142 | * @return stdClass A Joomla HAL object suitable for serialisation.
143 | */
144 | public function getHal()
145 | {
146 | $this->set('_meta', $this->meta);
147 |
148 | $hal = parent::getHal();
149 |
150 | return $hal;
151 | }
152 |
153 | /**
154 | * Method to return a metadata field.
155 | *
156 | * @param string $field Field name.
157 | * @param string $default Optional default value.
158 | *
159 | * @return mixed Value of field.
160 | */
161 | public function getMetadata($field, $default = '')
162 | {
163 | if (!isset($this->meta->$field))
164 | {
165 | return $default;
166 | }
167 |
168 | return $this->meta->$field;
169 | }
170 |
171 | /**
172 | * Method to return the resource map object.
173 | *
174 | * @return ApiApplicationResourcemap Resource map object.
175 | */
176 | public function getResourceMap()
177 | {
178 | return $this->resourceMap;
179 | }
180 |
181 | /**
182 | * Method to load an object into this HAL object.
183 | *
184 | * @param object $object Object whose properties are to be loaded.
185 | *
186 | * @return object This method may be chained.
187 | */
188 | public function load($object)
189 | {
190 | // If there is no map then use the standard load method.
191 | if (empty($this->resourceMap))
192 | {
193 | return parent::load($object);
194 | }
195 |
196 | parent::load($this->resourceMap->toExternal($object));
197 |
198 | return $this;
199 | }
200 |
201 | /**
202 | * Method to add or modify a metadata field.
203 | *
204 | * @param string $field Field name.
205 | * @param mixed $value Value to be assigned to the field.
206 | *
207 | * @return object This method may be chained.
208 | */
209 | public function setMetadata($field, $value)
210 | {
211 | $this->meta->$field = $value;
212 |
213 | return $this;
214 | }
215 |
216 | /**
217 | * Set pagination variables.
218 | *
219 | * @param array $page Array of pagination variables.
220 | *
221 | * @return object This object may be chained.
222 | */
223 | public function setPagination($page = array())
224 | {
225 | if (isset($page['page']))
226 | {
227 | $this->meta->page = $page['page'];
228 | }
229 |
230 | if (isset($page['perPage']))
231 | {
232 | $this->meta->perPage = $page['perPage'];
233 | }
234 |
235 | if (isset($page['offset']))
236 | {
237 | $this->meta->offset = $page['offset'];
238 | }
239 |
240 | if (isset($page['totalItems']))
241 | {
242 | $this->meta->totalItems = $page['totalItems'];
243 | }
244 |
245 | if (isset($page['totalPages']))
246 | {
247 | $this->meta->totalPages = $page['totalPages'];
248 | }
249 |
250 | return $this;
251 | }
252 |
253 | }
254 |
--------------------------------------------------------------------------------
/api/application/hal/link.php:
--------------------------------------------------------------------------------
1 | rel = $rel;
33 | $this->href = $href;
34 | }
35 |
36 | /**
37 | * Returns rel value.
38 | */
39 | public function getRel()
40 | {
41 | return $this->rel;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/api/application/includemap.php:
--------------------------------------------------------------------------------
1 | map = $map['embedded'];
37 | }
38 |
39 | return $this;
40 | }
41 |
42 | /**
43 | * Method to return a simple array of included fields.
44 | *
45 | * @return array Array of included field names.
46 | */
47 | public function toArray()
48 | {
49 | return $this->map;
50 | }
51 |
52 | /**
53 | * Method to determine if a particular map is to be included.
54 | *
55 | * @param string $fieldName Field name.
56 | *
57 | * @return boolean
58 | */
59 | public function isIncluded($fieldName)
60 | {
61 | return isset($this->map[$fieldName]);
62 | }
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/api/application/resourcemap.php:
--------------------------------------------------------------------------------
1 | basePath = isset($options['basePath']) ? $options['basePath'] : '';
39 | }
40 |
41 | /**
42 | * Load resource map from JSON.
43 | *
44 | * @param string JSON-encoded resource map.
45 | *
46 | * @return object This method may be chained.
47 | */
48 | public function fromJson($json)
49 | {
50 | $this->map = json_decode($json, true);
51 |
52 | return $this;
53 | }
54 |
55 | /**
56 | * Method to return the untransformed value associated with a field name.
57 | *
58 | * @param string $fieldName Field name.
59 | * @param mixed $default Optional default value.
60 | *
61 | * @return mixed Value associated with field name.
62 | */
63 | public function getField($fieldName, $default = '')
64 | {
65 | return isset($this->map[$fieldName]) ? $this->map[$fieldName] : $default;
66 | }
67 |
68 | /**
69 | * Method to extract a value from the source data.
70 | *
71 | * @param string $sourceDefinition Source data field name.
72 | * @param array $sourceData Source data.
73 | *
74 | * @return mixed Requested data field.
75 | */
76 | private function getSourceValue($sourceDefinition, $sourceData)
77 | {
78 | // Source definition fields must be in the form type:definition.
79 | // Locate first occurrence of a colon.
80 | $pos = strpos($sourceDefinition, ':');
81 |
82 | // Separate type and definition.
83 | $sourceFieldType = substr($sourceDefinition, 0, $pos);
84 | $definition = substr($sourceDefinition, $pos+1);
85 |
86 | // Look for source field names. These are surrounded by curly brackets.
87 | preg_match_all('/\{(.*)\}/U', $definition, $matches);
88 |
89 | // If the definition contains field names, substitute their values.
90 | if (!empty($matches[0]))
91 | {
92 | foreach ($matches[1] as $key => $fieldName)
93 | {
94 | $matches[1][$key] = $this->getValue($fieldName, $sourceData);
95 | }
96 |
97 | // Replace {fieldName} with value.
98 | $definition = str_replace($matches[0], $matches[1], $definition);
99 | }
100 |
101 | // Transform the value depending on its type.
102 | $return = $this->transformField($sourceFieldType, $definition, $sourceData);
103 |
104 | return $return;
105 | }
106 |
107 | /**
108 | * Get the name of the transform class for a given field type.
109 | *
110 | * First looks for the transform class in the /transform directory
111 | * in the same directory as the resource.json file. Then looks
112 | * for it in the /api/transform directory.
113 | *
114 | * @param string $fieldType Field type.
115 | *
116 | * @return string Transform class name.
117 | */
118 | private function getTransformClass($fieldType)
119 | {
120 | // Cache for the class names.
121 | static $classNames = array();
122 |
123 | // Cache for component class prefix.
124 | static $componentPrefix = '';
125 |
126 | // If we already know the class name, just return it.
127 | if (isset($classNames[$fieldType]))
128 | {
129 | return $classNames[$fieldType];
130 | }
131 |
132 | // Compute the component class prefix if needed.
133 | // This will be used for component-level overrides.
134 | if ($componentPrefix == '')
135 | {
136 | // Get the path to the resource.json file.
137 | $path = str_replace(JPATH_BASE, '', realpath($this->basePath));
138 |
139 | // Explode it and make some adjustments.
140 | $parts = explode('/', $path);
141 |
142 | foreach ($parts as $k => $part)
143 | {
144 | $parts[$k] = ucfirst(str_replace('com_', '', $part));
145 | if ($part == 'components')
146 | {
147 | $parts[$k] = 'Component';
148 | }
149 | if ($part == 'services')
150 | {
151 | unset($parts[$k]);
152 | }
153 | }
154 |
155 | $componentPrefix = implode('', $parts);
156 | }
157 |
158 | // Construct the name of the class to do the transform (default is ApiTransformString).
159 | $className = $componentPrefix . 'Transform' . ucfirst($fieldType);
160 | if (!class_exists($className))
161 | {
162 | $className = 'ApiTransform' . ucfirst($fieldType);
163 | }
164 |
165 | // Cache it for later.
166 | $classNames[$fieldType] = $className;
167 |
168 | return $className;
169 | }
170 |
171 | /**
172 | * Method to get a value from the source data.
173 | *
174 | * @param string $fieldName Name of the field.
175 | * @param string $data Data.
176 | *
177 | * @return mixed Field value (or null if not found).
178 | */
179 | private function getValue($fieldName, $data)
180 | {
181 | // Static array of unpacked json fields.
182 | static $unpacked = array();
183 |
184 | $return = null;
185 |
186 | // Look for an optional field separator in name.
187 | // The dot separator indicates that the prefix is a json-encoded
188 | // field, each element of which can be addressed by the suffix.
189 | if (strpos($fieldName, '.') !== false)
190 | {
191 | // Extract the field names.
192 | list($context, $fieldName) = explode('.', $fieldName);
193 |
194 | // Make sure we have unpacked the json field.
195 | if (!isset($unpacked[$context]))
196 | {
197 | $unpacked[$context] = json_decode($data->$context);
198 | }
199 |
200 | if (isset($unpacked[$context]->$fieldName))
201 | {
202 | $return = $unpacked[$context]->$fieldName;
203 | }
204 | }
205 | else
206 | {
207 | // If the field does not exist, return null.
208 | if (isset($data->$fieldName))
209 | {
210 | $return = $data->$fieldName;
211 | }
212 | }
213 |
214 | return $return;
215 | }
216 |
217 | /**
218 | * Method to determine if a particular map is available.
219 | *
220 | * @param string $fieldName Field name.
221 | *
222 | * @return boolean
223 | */
224 | public function isAvailable($fieldName)
225 | {
226 | return isset($this->map[$fieldName]);
227 | }
228 |
229 | /**
230 | * Transform a data object to its external representation.
231 | *
232 | * @param object $data Data object.
233 | * @param array $include List of fields to include (if empty then include all fields).
234 | *
235 | * @return object External representation of the data object.
236 | */
237 | public function toExternal($data, array $include = array())
238 | {
239 | // If there is no map then return the data unmodified.
240 | if (empty($this->map))
241 | {
242 | return $data;
243 | }
244 |
245 | // Initialise the object store.
246 | $targetData = array();
247 |
248 | // Run through each mapped field.
249 | foreach ($this->map as $halField => $sourceDefinition)
250 | {
251 | // Check that the field is to be included.
252 | if (!empty($include) && !in_array($halField, $include))
253 | {
254 | continue;
255 | }
256 |
257 | // Left-hand side (HAL field) must be in the form objectName/name.
258 | // Note that objectName is optional; default is "main".
259 | list($halFieldPath, $halFieldName) = explode('/', $halField);
260 |
261 | // If we have a non-empty objectName then make sure we have an object with that name.
262 | $targetContext = $halFieldPath == '' ? 'main' : $halFieldPath;
263 | if (!isset($targetData[$targetContext]))
264 | {
265 | $targetData[$targetContext] = new stdClass;
266 | }
267 |
268 | // Look for an optional field separator in name.
269 | // The dot separator indicates that the prefix is an object
270 | // and the suffix is a property of that object.
271 | if (strpos($halFieldName, '.') !== false)
272 | {
273 | // Extract the field names.
274 | list($halFieldObject, $halFieldProperty) = explode('.', $halFieldName);
275 |
276 | // If the object doesn't already exist, create it.
277 | if (!isset($targetData[$targetContext]->$halFieldObject))
278 | {
279 | $targetData[$targetContext]->$halFieldObject = new stdClass;
280 | }
281 |
282 | // Extract source data into object property.
283 | $targetData[$targetContext]->$halFieldObject->$halFieldProperty = $this->getSourceValue($sourceDefinition, $data);
284 | }
285 | else
286 | {
287 | // Extract source data into simple field.
288 | $targetData[$targetContext]->$halFieldName = $this->getSourceValue($sourceDefinition, $data);
289 | }
290 | }
291 |
292 | // Remove any redundant _links.
293 | if (isset($targetData['_links']))
294 | {
295 | foreach ($targetData['_links'] as $k => $link)
296 | {
297 | if (!isset($link->href) || $link->href == '')
298 | {
299 | unset($targetData['_links']->$k);
300 | }
301 | }
302 | }
303 |
304 | // Move subsidiary objects under main object so it has the right structure when serialised.
305 | foreach ($targetData as $objName => $object)
306 | {
307 | if ($objName != 'main')
308 | {
309 | $targetData['main']->$objName = $targetData[$objName];
310 | unset( $targetData[$objName]);
311 | }
312 | }
313 |
314 | return $targetData['main'];
315 | }
316 |
317 | /**
318 | * Transform a data object to its internal representation.
319 | *
320 | * @param object $data Data object.
321 | *
322 | * @return mixed Internal representation of the data object.
323 | */
324 | public function toInternal($data)
325 | {
326 | // If there is no map then return the data unmodified.
327 | if (empty($this->map))
328 | {
329 | return $data;
330 | }
331 |
332 | // @TODO
333 | }
334 |
335 | /**
336 | * Transform a source field data value.
337 | *
338 | * Calls the static toExternal method of a transform class.
339 | *
340 | * @param string $fieldType Field type.
341 | * @param string $definition Field definition.
342 | * @param string $data Data to be transformed.
343 | *
344 | * @return mixed Transformed data.
345 | */
346 | private function transformField($fieldType, $definition, $data)
347 | {
348 | // Get the transform class name.
349 | $className = $this->getTransformClass($fieldType);
350 |
351 | // Execute the transform.
352 | if ($className instanceof ApiTransform)
353 | {
354 | return $className::toExternal($definition, $data);
355 | }
356 | else
357 | {
358 | return $definition;
359 | }
360 | }
361 | }
362 |
--------------------------------------------------------------------------------
/api/application/router.php:
--------------------------------------------------------------------------------
1 | controllerPrefix = 'Component' . ucfirst($parts[1]);
38 | $controller = $parts[2];
39 | }
40 |
41 | return $controller;
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/api/application/web.php:
--------------------------------------------------------------------------------
1 | _startTime = microtime(true);
67 |
68 | parent::__construct($input, $config, $client);
69 |
70 | // Load the Joomla CMS configuration object.
71 | $this->loadConfiguration($this->fetchConfigurationData());
72 |
73 | // By default, assume response may be cached.
74 | $this->allowCache(true);
75 | }
76 |
77 | /**
78 | * Permits retrieval of the database connection for this application.
79 | *
80 | * @return JDatabaseDriver The database driver.
81 | *
82 | * @since 3.2
83 | */
84 | public function getDatabase()
85 | {
86 | return $this->db;
87 | }
88 |
89 | /**
90 | * Allows the application to load a custom or default database driver.
91 | *
92 | * @param JDatabaseDriver $driver An optional database driver object. If omitted, the application driver is created.
93 | *
94 | * @return object This method may be chained.
95 | *
96 | * @since 3.2
97 | */
98 | public function loadDatabase(JDatabaseDriver $driver = null)
99 | {
100 | if ($driver === null)
101 | {
102 | $this->db = JDatabaseDriver::getInstance(
103 | array(
104 | 'driver' => $this->get('dbtype'),
105 | 'host' => $this->get('host'),
106 | 'user' => $this->get('user'),
107 | 'password' => $this->get('password'),
108 | 'database' => $this->get('db'),
109 | 'prefix' => $this->get('dbprefix'),
110 | // 'schema' => $this->get('db_schema'),
111 | // 'port' => $this->get('db_port')
112 | )
113 | );
114 |
115 | // Select the database.
116 | $this->db->select($this->get('db'));
117 | }
118 | // Use the given database driver object.
119 | else
120 | {
121 | $this->db = $driver;
122 | }
123 |
124 | // Set the database to our static cache.
125 | JFactory::$database = $this->db;
126 |
127 | return $this;
128 | }
129 |
130 | /**
131 | * Method to load services route maps.
132 | *
133 | * @param array $maps A list of route maps to add to the router as $pattern => $controller.
134 | *
135 | * @return object This method may be chained.
136 | *
137 | * @since 3.2
138 | */
139 | protected function loadMaps($maps = array())
140 | {
141 | // Make sure we have an array.
142 | $maps = (array) $maps;
143 |
144 | // If route indicates a traditional Joomla component then register special prefix.
145 | foreach ($maps as $key => $route)
146 | {
147 | $parts = explode('/', $route);
148 | if ($parts[0] == 'component')
149 | {
150 | $path = JPATH_SITE . '/components/com_' . $parts[1] . '/services';
151 | JLoader::registerPrefix('Component' . ucfirst($parts[1]), $path);
152 | }
153 | }
154 |
155 | $this->maps = array_merge($this->maps, $maps);
156 |
157 | return $this;
158 | }
159 |
160 | /**
161 | * Allows the application to load a custom or default router.
162 | *
163 | * @param JApplicationWebRouter $router An optional router object. If omitted, the standard router is created.
164 | *
165 | * @return object This method may be chained.
166 | *
167 | * @since 3.2
168 | */
169 | public function loadRouter(JApplicationWebRouter $router = null)
170 | {
171 | $this->router = ($router === null) ? new ApiApplicationRouter($this, $this->input) : $router;
172 |
173 | return $this;
174 | }
175 |
176 | /**
177 | * Allows the application to load a custom or default dispatcher.
178 | *
179 | * The logic and options for creating this object are adequately generic for default cases
180 | * but for many applications it will make sense to override this method and create event
181 | * dispatchers, if required, based on more specific needs.
182 | *
183 | * @param JEventDispatcher $dispatcher An optional dispatcher object. If omitted, the factory dispatcher is created.
184 | *
185 | * @return JApplicationBase This method is chainable.
186 | *
187 | * @since 12.1
188 | */
189 | public function loadDispatcher(JEventDispatcher $dispatcher = null)
190 | {
191 | $this->dispatcher = ($dispatcher === null) ? JEventDispatcher::getInstance() : $dispatcher;
192 |
193 | // Trigger the authentication plugins.
194 | JPluginHelper::importPlugin('authentication');
195 |
196 | return $this;
197 | }
198 |
199 | /**
200 | * Execute the application.
201 | *
202 | * @return void
203 | *
204 | * @since 3.2
205 | */
206 | protected function doExecute()
207 | {
208 | $documentOptions = array(
209 | 'absoluteHrefs' => $this->get('absoluteHrefs', false),
210 | );
211 |
212 | try
213 | {
214 | // Set the controller prefix, add maps, and execute the appropriate controller.
215 | $this->input = new JInputJson;
216 | $this->document = new ApiDocumentHalJson($documentOptions);
217 | $this->router->setControllerPrefix('ApiServices')
218 | ->setDefaultController('Root')
219 | ->addMaps($this->maps)
220 | ->execute($this->get('uri.route'));
221 | }
222 | catch (Exception $e)
223 | {
224 | $this->setHeader('status', '400', true);
225 | $message = $e->getMessage();
226 | $body = array('message' => $message, 'code' => $e->getCode(), 'type' => get_class($e));
227 |
228 | $this->setBody(json_encode($body));
229 | }
230 | }
231 |
232 | /**
233 | * Method to get the application configuration data to be loaded.
234 | *
235 | * @param string $file The path and filename of the configuration file. If not provided, configuration.php
236 | * in JPATH_BASE will be used.
237 | * @param string $class The class name to instantiate.
238 | *
239 | * @return object An object to be loaded into the application configuration.
240 | *
241 | * @since 3.2
242 | */
243 | public function fetchApiConfigurationData($file = '', $class = 'JConfig')
244 | {
245 | // Instantiate variables.
246 | $config = array();
247 |
248 | // Ensure that required path constants are defined.
249 | if (!defined('JPATH_CONFIGURATION'))
250 | {
251 | $path = getenv('JAPI_CONFIG');
252 | if ($path)
253 | {
254 | define('JPATH_CONFIGURATION', realpath($path));
255 | }
256 | else
257 | {
258 | define('JPATH_CONFIGURATION', realpath(dirname(JPATH_BASE) . '/config'));
259 | }
260 | }
261 |
262 | // Set the configuration file path for the application.
263 | if (file_exists(JPATH_CONFIGURATION . '/config.json'))
264 | {
265 | $file = JPATH_CONFIGURATION . '/config.json';
266 | }
267 | else
268 | {
269 | $file = JPATH_CONFIGURATION . '/config.dist.json';
270 | }
271 |
272 | if (!is_readable($file))
273 | {
274 | throw new RuntimeException('Configuration file does not exist or is unreadable.');
275 | }
276 |
277 | // Load the configuration file into an object.
278 | $config = json_decode(file_get_contents($file));
279 |
280 | return $config;
281 | }
282 |
283 | /**
284 | * Method to load services route maps from all subdirectories
285 | * within a given directory (non-recursive).
286 | *
287 | * @param string $basePath Path to base directory.
288 | *
289 | * @return object This method may be chained.
290 | *
291 | * @since 3.2
292 | */
293 | protected function fetchMaps($basePath = JPATH_SITE)
294 | {
295 | // Get a directory iterator for the base path.
296 | $iterator = new DirectoryIterator($basePath);
297 |
298 | // Iterate over the files, looking for just the directories.
299 | foreach ($iterator as $file)
300 | {
301 | $fileName = $file->getFilename();
302 |
303 | // Only want directories.
304 | if ($file->isDir())
305 | {
306 | // Look for services file.
307 | $servicesFilename = $basePath . '/' . $fileName . '/services.json';
308 | if (file_exists($servicesFilename))
309 | {
310 | $this->loadMaps(json_decode(file_get_contents($servicesFilename), true));
311 | }
312 | }
313 | }
314 |
315 | return $this;
316 | }
317 |
318 | /**
319 | * Method to load services route maps from standard locations.
320 | *
321 | * @return object This method may be chained.
322 | *
323 | * @since 3.2
324 | */
325 | public function fetchStandardMaps()
326 | {
327 | // Look for maps in front-end components.
328 | $this->fetchMaps(JPATH_SITE . '/components');
329 |
330 | // Look for maps in back-end components.
331 | $this->fetchMaps(JPATH_ADMINISTRATOR . '/components');
332 |
333 | // Merge the main services file.
334 | $this->loadMaps(json_decode(file_get_contents(JPATH_CONFIGURATION . '/services.json'), true));
335 |
336 | return $this;
337 | }
338 |
339 | /**
340 | * Method to get services route maps.
341 | *
342 | * @return array A list of route maps to add to the router as $pattern => $controller.
343 | *
344 | * @since 3.2
345 | */
346 | public function getMaps()
347 | {
348 | return $this->maps;
349 | }
350 |
351 | /**
352 | * Method to send the application response to the client. All headers will be sent prior to the main
353 | * application output data.
354 | *
355 | * @return void
356 | *
357 | * @since 3.2
358 | */
359 | protected function respond()
360 | {
361 | $runtime = microtime(true) - $this->_startTime;
362 |
363 | // Set the Server and X-Powered-By Header.
364 | $this->setHeader('Server', '', true);
365 | $this->setHeader('X-Powered-By', 'JoomlaWebAPI/1.0', true);
366 | $this->setHeader('X-Runtime', $runtime, true);
367 | $this->setHeader('Access-Control-Allow-Origin', '*', true);
368 |
369 | // Copy document encoding and charset into application.
370 | $this->mimeType = $this->getDocument()->getMimeEncoding();
371 | $this->charSet = $this->getDocument()->getCharset();
372 |
373 | parent::respond();
374 | }
375 | }
376 |
--------------------------------------------------------------------------------
/api/controller/base.php:
--------------------------------------------------------------------------------
1 | getService();
56 |
57 | // Push results into the document.
58 | $this->app->getDocument()
59 | ->setMimeEncoding($this->contentType) // Comment this line out to debug
60 | ->setBuffer($service->getHal())
61 | ;
62 | }
63 |
64 | /**
65 | * Get API query object.
66 | *
67 | * Returns the API query helper object, creating a new
68 | * one if it doesn't already exist.
69 | * May be overridden in child classes.
70 | *
71 | * @return ApiDatabaseQuery object;
72 | */
73 | public function getApiQuery()
74 | {
75 | if (is_null($this->apiQuery))
76 | {
77 | // Get a database query helper object.
78 | $this->apiQuery = new ApiDatabaseQuery($this->db);
79 | }
80 |
81 | return $this->apiQuery;
82 | }
83 |
84 | /**
85 | * Get resource data.
86 | *
87 | * May be overridden in child classes.
88 | *
89 | * @return object Resource data.
90 | */
91 | public function getData()
92 | {
93 | }
94 |
95 | /**
96 | * Get database query.
97 | *
98 | * Returns a new base query for the table name given.
99 | * May be overridden in child classes.
100 | *
101 | * @param string $table Primary table name.
102 | *
103 | * @return JDatabaseDriver object.
104 | */
105 | public function getQuery($table)
106 | {
107 | // Create a database query object.
108 | $query = $this->db->getQuery(true)
109 | ->select('*')
110 | ->from($this->db->qn($table) . ' as p')
111 | ;
112 |
113 | return $query;
114 | }
115 |
116 | /**
117 | * Get service object.
118 | *
119 | * May be overridden in child classes.
120 | *
121 | * @return ApiDatabaseQuery object;
122 | */
123 | public function getService()
124 | {
125 | if (is_null($this->service))
126 | {
127 | $this->service = new ApiApplicationHalJoomla($this->serviceOptions);
128 | }
129 |
130 | return $this->service;
131 | }
132 |
133 | /**
134 | * Set the database driver to use.
135 | *
136 | * @param JDatabaseDriver $db Database driver.
137 | *
138 | * @return object This method may be chained.
139 | */
140 | public function setDatabase(JDatabaseDriver $db = null)
141 | {
142 | $this->db = isset($db) ? $db : $this->app->getDatabase();
143 |
144 | return $this;
145 | }
146 |
147 | /**
148 | * Set controller options.
149 | *
150 | * @param array $options Array of controller options.
151 | *
152 | * @return object This method may be chained.
153 | */
154 | public function setOptions($options = array())
155 | {
156 | // Setup dependencies.
157 | $this->serviceOptions = (array) $options;
158 |
159 | // Set primary table name.
160 | if (isset($options['tableName']))
161 | {
162 | $this->tableName = $options['tableName'];
163 | }
164 |
165 | // Set the content type.
166 | if (isset($options['contentType']))
167 | {
168 | $this->contentType = $options['contentType'];
169 | }
170 |
171 | // Set the primary relation.
172 | if (isset($options['primaryRel']))
173 | {
174 | $this->primaryRel = $options['primaryRel'];
175 | }
176 |
177 | return $this;
178 |
179 | }
180 |
181 | }
--------------------------------------------------------------------------------
/api/controller/controller.php:
--------------------------------------------------------------------------------
1 | id = (int) $this->input->get('id');
23 |
24 | // Get resource item data.
25 | $data = $this->getData();
26 |
27 | // Get service object.
28 | $service = $this->getService();
29 |
30 | // Load the data into the HAL object.
31 | $service->load($data);
32 |
33 | parent::execute();
34 | }
35 |
36 | /**
37 | * Get data for a single resource item.
38 | *
39 | * @return object Single resource item object.
40 | */
41 | public function getData()
42 | {
43 | // Get the database query object.
44 | $query = $this->getQuery($this->tableName);
45 |
46 | // Get a database query helper object.
47 | $apiQuery = $this->getApiQuery();
48 |
49 | // Get single record from database.
50 | $data = $apiQuery->getItem($query, (int) $this->id);
51 |
52 | return $data;
53 | }
54 |
55 | }
--------------------------------------------------------------------------------
/api/controller/list.php:
--------------------------------------------------------------------------------
1 | catid = (int) $this->input->get('catid');
23 |
24 | // Get page of data.
25 | $data = $this->getData();
26 |
27 | // Get service object.
28 | $service = $this->getService();
29 |
30 | // Set pagination.
31 | $service->setPagination($this->getApiQuery()->getPagination());
32 |
33 | // Import the data into the HAL object.
34 | $service->embed($this->primaryRel, $data);
35 |
36 | parent::execute();
37 | }
38 |
39 | /**
40 | * Get a page of data.
41 | *
42 | * @return array Array of data records.
43 | */
44 | public function getData()
45 | {
46 | // Get the database query object.
47 | $query = $this->getQuery($this->tableName);
48 |
49 | // Get a database query helper object.
50 | $apiQuery = $this->getApiQuery();
51 |
52 | // Get pagination variables.
53 | $pagination = $this->getPagination();
54 |
55 | // Get the page of data.
56 | $data = $apiQuery->setPagination($pagination)->getList($query);
57 |
58 | return $data;
59 | }
60 |
61 | /**
62 | * Get pagination variables.
63 | *
64 | * May be overridden in child classes.
65 | *
66 | * @return array Array of pagination variables.
67 | */
68 | public function getPagination()
69 | {
70 | // Set pagination variables from input.
71 | $pagination = array(
72 | 'offset' => (int) $this->input->get('offset', 0),
73 | 'page' => (int) $this->input->get('page', 1),
74 | 'perPage' => (int) $this->input->get('perPage', 15),
75 | );
76 |
77 | return $pagination;
78 | }
79 |
80 | /**
81 | * Get database query.
82 | *
83 | * May be overridden in child classes.
84 | *
85 | * @param string $table Primary table name.
86 | *
87 | * @return JDatabaseDriver object.
88 | */
89 | public function getQuery($table)
90 | {
91 | // Get the base query.
92 | $query = parent::getQuery($table);
93 |
94 | if ($this->catid)
95 | {
96 | $query->where($this->db->qn('catid') . ' = ' . (int) $this->catid);
97 | }
98 |
99 | return $query;
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/api/database/query.php:
--------------------------------------------------------------------------------
1 | db = $db;
35 |
36 | // Initialise pagination array.
37 | $this->pagination = array(
38 | 'offset' => 0,
39 | 'page' => 1,
40 | 'perPage' => 10,
41 | );
42 | }
43 |
44 | /**
45 | * Get single data record.
46 | *
47 | * Given a base query this method will return the single
48 | * data record with the given value of a unique key.
49 | *
50 | * @param JDatabaseQuery $query A database query object.
51 | * @param integer $id Unique key value.
52 | * @param string $pk Key name.
53 | *
54 | * @return object Single resource item object.
55 | */
56 | public function getItem(JDatabaseQuery $query, $id, $pk = 'id')
57 | {
58 | // Apply key to query.
59 | $itemQuery = clone($query);
60 | $itemQuery->where($this->db->qn($pk) . ' = ' . (int) $id);
61 |
62 | // Retrieve the data.
63 | $data = $this->db
64 | ->setQuery($itemQuery)
65 | ->loadObject();
66 |
67 | return $data;
68 | }
69 |
70 | /**
71 | * Get page of data.
72 | *
73 | * Given a base query this method will apply current pagination
74 | * variables to return a page of data records.
75 | *
76 | * @param JDatabaseQuery $query A database query object.
77 | *
78 | * @return Array of data objects returned by the query.
79 | */
80 | public function getList(JDatabaseQuery $query)
81 | {
82 | // Apply sanity check to perPage.
83 | $this->pagination['perPage'] = min(max($this->pagination['perPage'], 1), 100);
84 |
85 | // Determine total items and total pages.
86 | $countQuery = clone($query);
87 | $countQuery->clear('select')->select('count(*)');
88 | $this->pagination['totalItems'] = (int) $this->db->setQuery($countQuery)->loadResult();
89 | $this->pagination['totalPages'] = (int) floor(($this->pagination['totalItems']-1)/$this->pagination['perPage']) + 1;
90 |
91 | // Apply sanity check to page number.
92 | $this->pagination['page'] = min(max($this->pagination['page'], 1), $this->pagination['totalPages']);
93 |
94 | // Calculate base for paginated query.
95 | $base = ($this->pagination['page'] - 1) * $this->pagination['perPage'] + $this->pagination['offset'];
96 |
97 | // Retrieve the data.
98 | $data = $this->db
99 | ->setQuery($query, $base, $this->pagination['perPage'])
100 | ->loadObjectList();
101 |
102 | return $data;
103 | }
104 |
105 | /**
106 | * Return array of pagination variables.
107 | *
108 | * @return array Array of pagination variables.
109 | */
110 | public function getPagination()
111 | {
112 | return $this->pagination;
113 | }
114 |
115 | /**
116 | * Set pagination variables.
117 | *
118 | * @param array $pagination Array of pagination variables.
119 | *
120 | * @return object This object may be chained.
121 | */
122 | public function setPagination($pagination = array())
123 | {
124 | $this->pagination = array_merge($this->pagination, $pagination);
125 |
126 | return $this;
127 | }
128 |
129 | }
130 |
--------------------------------------------------------------------------------
/api/document/hal/json.php:
--------------------------------------------------------------------------------
1 | _mime = 'application/json';
45 |
46 | // Set document type.
47 | $this->_type = 'hal+json';
48 |
49 | // Set absolute/relative hrefs.
50 | $this->absoluteHrefs = isset($options['absoluteHrefs']) ? $options['absoluteHrefs'] : false;
51 | }
52 |
53 | /**
54 | * Render the document.
55 | *
56 | * @param boolean $cache If true, cache the output
57 | * @param array $params Associative array of attributes
58 | *
59 | * @return The rendered data
60 | *
61 | * @since 3.1
62 | */
63 | public function render($cache = false, $params = array())
64 | {
65 | JResponse::allowCache($cache);
66 | JResponse::setHeader('Content-disposition', 'attachment; filename="' . $this->getName() . '.json"', true);
67 |
68 | // Unfortunately, the exact syntax of the Content-Type header
69 | // is not defined, so we have to try to be a bit clever here.
70 | $contentType = $this->_mime;
71 | if (stripos($contentType, 'json') === false)
72 | {
73 | $contentType .= '+' . $this->_type;
74 | }
75 | $this->_mime = $contentType;
76 |
77 | parent::render();
78 |
79 | // Get the HAL object from the buffer.
80 | $hal = $this->getBuffer();
81 |
82 | // If required, change relative links to absolute.
83 | if ($this->absoluteHrefs && is_object($hal) && isset($hal->_links))
84 | {
85 | // Adjust hrefs in the _links object.
86 | $this->relToAbs($hal->_links);
87 |
88 | // Adjust hrefs in the _embedded object (if there is one).
89 | if (isset($hal->_embedded))
90 | {
91 | foreach ($hal->_embedded as $rel => $resources)
92 | {
93 | foreach ($resources as $id => $resource)
94 | {
95 | if (isset($resource->_links))
96 | {
97 | $this->relToAbs($resource->_links);
98 | }
99 | }
100 | }
101 | }
102 | }
103 |
104 | // Return it as a JSON string.
105 | return json_encode($hal);
106 | }
107 |
108 | /**
109 | * Returns the document name
110 | *
111 | * @return string
112 | *
113 | * @since 3.1
114 | */
115 | public function getName()
116 | {
117 | return $this->_name;
118 | }
119 |
120 | /**
121 | * Method to convert relative to absolute links.
122 | *
123 | * @param object $links Links object (eg. _links).
124 | */
125 | protected function relToAbs($links)
126 | {
127 | // Adjust hrefs in the _links object.
128 | foreach ($links as $rel => $link)
129 | {
130 | if (substr($link->href, 0, 1) == '/')
131 | {
132 | $links->$rel->href = rtrim(JUri::base(), '/') . $link->href;
133 | }
134 | }
135 | }
136 |
137 | /**
138 | * Sets the document name
139 | *
140 | * @param string $name Document name
141 | *
142 | * @return JDocumentJSON instance of $this to allow chaining
143 | *
144 | * @since 3.1
145 | */
146 | public function setName($name = 'joomla')
147 | {
148 | $this->_name = $name;
149 |
150 | return $this;
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/api/import.php:
--------------------------------------------------------------------------------
1 | loadSession()
95 | ->loadDatabase()
96 | ->loadIdentity()
97 | ->loadDispatcher()
98 | ->fetchStandardMaps()
99 | ->loadRouter()
100 | ->execute();
101 | }
102 | catch (Exception $e)
103 | {
104 | // Set the server response code.
105 | header('Status: 500', true, 500);
106 |
107 | // An exception has been caught, echo the message and exit.
108 | echo json_encode(array('message' => $e->getMessage(), 'code' => $e->getCode(), 'type' => get_class($e)));
109 | exit();
110 | }
111 |
--------------------------------------------------------------------------------
/api/services/categories/get.php:
--------------------------------------------------------------------------------
1 | setDatabase();
23 |
24 | // Set the controller options.
25 | $serviceOptions = array(
26 | 'contentType' => 'application/vnd.joomla.item.v1; schema=categories.v1',
27 | 'describedBy' => 'http://docs.joomla.org/Schemas/categories/v1',
28 | 'primaryRel' => 'joomla:categories',
29 | 'resourceMap' => __DIR__ . '/resource.json',
30 | 'self' => '/joomla:categories/' . (int) $this->input->get('id'),
31 | 'tableName' => '#__categories',
32 | );
33 |
34 | $this->setOptions($serviceOptions);
35 | }
36 |
37 | /**
38 | * Execute the request.
39 | */
40 | public function execute()
41 | {
42 | // Get resource item id from input.
43 | $this->id = (int) $this->input->get('id');
44 |
45 | // Get resource item data.
46 | $data = $this->getData();
47 |
48 | // Get service object.
49 | $service = $this->getService();
50 |
51 | // We need to add a link from the current category to the content items that
52 | // exist within the category. However, we only know the name of the extension
53 | // and not the resource name used by the API. We try to work out the correct
54 | // entry to make by doing a reverse-lookup on the router maps.
55 | if (isset($data->extension))
56 | {
57 | // Get the component name (without the com_ prefix).
58 | $extension = str_replace('com_', '', $data->extension);
59 |
60 | // Get the router maps.
61 | $maps = $this->app->getMaps();
62 |
63 | // Construct the regex pattern of the route we want to find.
64 | $pattern = '#component/' . $extension . '/(.*)List#';
65 |
66 | // Look for an appropriate route.
67 | foreach ($maps as $rel => $route)
68 | {
69 | if (substr($rel, 0, 17) == 'joomla:categories')
70 | {
71 | // Look for a match for our route.
72 | $matches = array();
73 | preg_match($pattern, $route, $matches);
74 |
75 | if (!empty($matches))
76 | {
77 | // Add a link to the resources associated with the category.
78 | $linkRel = 'joomla:' . strtolower($matches[1]);
79 | $linkHref = '/' . str_replace(':catid', $this->id, $rel);
80 | $service->addLink(new ApiApplicationHalLink($linkRel, $linkHref));
81 | }
82 | }
83 | }
84 | }
85 |
86 | // Load the data into the HAL object.
87 | $service->load($data);
88 |
89 | parent::execute();
90 | }
91 |
92 | }
93 |
--------------------------------------------------------------------------------
/api/services/categories/list/embedded.json:
--------------------------------------------------------------------------------
1 | {
2 | "embedded":
3 | [
4 | "/access",
5 | "/description",
6 | "/extension",
7 | "/language",
8 | "_links/self.href",
9 | "_links/joomla:checkout.href",
10 | "_links/joomla:checkout.method",
11 | "metadata/authorName",
12 | "/ordering",
13 | "publish/alias",
14 | "publish/created",
15 | "/state",
16 | "/title"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/api/services/categories/list/get.php:
--------------------------------------------------------------------------------
1 | setDatabase();
23 |
24 | // Set the controller options.
25 | $serviceOptions = array(
26 | 'contentType' => 'application/vnd.joomla.list.v1',
27 | 'describedBy' => 'http://docs.joomla.org/Schemas/categories/v1',
28 | 'embeddedMap' => __DIR__ . '/embedded.json',
29 | 'primaryRel' => 'joomla:categories',
30 | 'resourceMap' => realpath(__DIR__ . '/../resource.json'),
31 | 'self' => '/joomla:categories',
32 | 'tableName' => '#__categories',
33 | );
34 |
35 | $this->setOptions($serviceOptions);
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/api/services/categories/resource.json:
--------------------------------------------------------------------------------
1 | {
2 | "/access":"int:{access}",
3 | "/description":"string:{description}",
4 | "/extension":"string:{extension}",
5 | "/language":"string:{language}",
6 | "/level":"int:{level}",
7 | "_links/self.href":"string:/joomla:categories/{id}",
8 | "_links/joomla:assets.href":"string:/joomla:assets/{asset_id}",
9 | "_links/joomla:checkin.href":"string:/joomla:categories/{id}?checkin",
10 | "_links/joomla:checkin.method":"string:post",
11 | "_links/joomla:checkout.href":"string:/joomla:categories/{id}?checkout",
12 | "_links/joomla:checkout.method":"string:post",
13 | "_links/joomla:createdBy.href":"string:/joomla:users/{created_user_id}",
14 | "_links/joomla:image.href":"string:{params.image}",
15 | "_links/joomla:modifiedBy.href":"string:/joomla:users/{modified_user_id}",
16 | "metadata/authorName":"string:{metadata.author}",
17 | "metadata/description":"string:{metadesc}",
18 | "metadata/keywords":"string:{metakey}",
19 | "metadata/robots":"string:{metadata.robots}",
20 | "metadata/rights":"string:{metadata.rights}",
21 | "/note":"string:{note}",
22 | "/path":"string:{path}",
23 | "publish/alias":"string:{alias}",
24 | "publish/created":"datetime:{created_time}",
25 | "publish/hits":"int:{hits}",
26 | "publish/revision":"int:{version}",
27 | "render/target":"target:{params.target}",
28 | "/state":"state:{published}",
29 | "/title":"string:{title}"
30 | }
31 |
--------------------------------------------------------------------------------
/api/services/menuitems/get.php:
--------------------------------------------------------------------------------
1 | setDatabase();
23 |
24 | // Set the controller options.
25 | $serviceOptions = array(
26 | 'contentType' => 'application/vnd.joomla.item.v1; schema=menuitems.v1',
27 | 'describedBy' => 'http://docs.joomla.org/Schemas/menuitems/v1',
28 | 'primaryRel' => 'joomla:menuitems',
29 | 'resourceMap' => __DIR__ . '/resource.json',
30 | 'self' => '/joomla:menuitems/' . (int) $this->input->get('id'),
31 | 'tableName' => '#__menu',
32 | );
33 |
34 | $this->setOptions($serviceOptions);
35 | }
36 |
37 | /**
38 | * Get a single record from database.
39 | *
40 | * @return array Array of data records.
41 | */
42 | public function getData()
43 | {
44 | // Get the database query object.
45 | $query = $this->getQuery($this->tableName);
46 |
47 | // Get a database query helper object.
48 | $apiQuery = $this->getApiQuery();
49 |
50 | // Get single record from database.
51 | $data = $apiQuery->getItem($query, (int) $this->input->get('id'), 'm.id');
52 |
53 | return $data;
54 | }
55 |
56 | /**
57 | * Get database query.
58 | *
59 | * @param string $table Primary table name.
60 | *
61 | * @return JDatabaseDriver object.
62 | */
63 | public function getQuery($table)
64 | {
65 | // Create a database query object.
66 | $query = $this->db->getQuery(true)
67 | ->select('m.*, mt.id AS menu_id')
68 | ->from('#__menu AS m')
69 | ->leftjoin('#__menu_types AS mt ON m.menutype = mt.menutype')
70 | ;
71 |
72 | return $query;
73 | }
74 |
75 | }
76 |
--------------------------------------------------------------------------------
/api/services/menuitems/list/embedded.json:
--------------------------------------------------------------------------------
1 | {
2 | "embedded":
3 | [
4 | "/access",
5 | "/language",
6 | "/level",
7 | "_links/self.href",
8 | "_links/joomla:checkout.href",
9 | "_links/joomla:checkout.method",
10 | "_links/joomla:image.href",
11 | "_links/joomla:menus.href",
12 | "publish/alias",
13 | "/state",
14 | "/title",
15 | "/type"
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/api/services/menuitems/list/get.php:
--------------------------------------------------------------------------------
1 | setDatabase();
23 |
24 | // Set the controller options.
25 | $serviceOptions = array(
26 | 'contentType' => 'application/vnd.joomla.list.v1',
27 | 'describedBy' => 'http://docs.joomla.org/Schemas/menuitems/v1',
28 | 'embeddedMap' => __DIR__ . '/embedded.json',
29 | 'primaryRel' => 'joomla:menuitems',
30 | 'resourceMap' => realpath(__DIR__ . '/../resource.json'),
31 | 'self' => '/joomla:menuitems',
32 | 'tableName' => '#__menu',
33 | );
34 |
35 | $this->setOptions($serviceOptions);
36 | }
37 |
38 | /**
39 | * Get database query.
40 | *
41 | * @param string $table Primary table name.
42 | *
43 | * @return JDatabaseDriver object.
44 | */
45 | public function getQuery($table)
46 | {
47 | // Create a database query object.
48 | $query = $this->db->getQuery(true)
49 | ->select('m.*, mt.id AS menu_id')
50 | ->from('#__menu AS m')
51 | ->leftjoin('#__menu_types AS mt ON m.menutype = mt.menutype')
52 | ;
53 |
54 | return $query;
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/api/services/menuitems/resource.json:
--------------------------------------------------------------------------------
1 | {
2 | "/access":"int:{access}",
3 | "/language":"string:{language}",
4 | "/level":"int:{level}",
5 | "_links/self.href":"string:/joomla:menuitems/{id}",
6 | "_links/joomla:checkin.href":"string:/joomla:menuitems/{id}?checkin",
7 | "_links/joomla:checkin.method":"string:post",
8 | "_links/joomla:checkout.href":"string:/joomla:menuitems/{id}?checkout",
9 | "_links/joomla:checkout.method":"string:post",
10 | "_links/joomla:image.href":"string:{img}",
11 | "_links/joomla:menus.href":"string:/joomla:menus/{menu_id}",
12 | "/note":"string:{note}",
13 | "publish/alias":"string:{alias}",
14 | "render/linkTitle":"string:{params.menu-anchor_title}",
15 | "render/linkCss":"string:{params.menu-anchor_css}",
16 | "/state":"state:{published}",
17 | "/title":"string:{title}",
18 | "/type":"string:{type}"
19 | }
20 |
--------------------------------------------------------------------------------
/api/services/root/get.php:
--------------------------------------------------------------------------------
1 | 'application/vnd.joomla.service.v1',
24 | 'describedBy' => 'http://docs.joomla.org/Schemas/service/v1',
25 | 'self' => '/',
26 | );
27 |
28 | $this->setOptions($serviceOptions);
29 | }
30 |
31 | /**
32 | * Execute the request.
33 | */
34 | public function execute()
35 | {
36 | // Get service object.
37 | $service = $this->getService();
38 |
39 | // Look for the top-level resources and add them as links.
40 | foreach ($this->app->getMaps() as $route => $map)
41 | {
42 | if (strpos($route, '/') === false)
43 | {
44 | $service->addLink(new ApiApplicationHalLink($route, '/' . $route));
45 | }
46 | }
47 |
48 | parent::execute();
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/api/transform/base.php:
--------------------------------------------------------------------------------
1 | setDatabase();
23 |
24 | // Set the controller options.
25 | $serviceOptions = array(
26 | 'contentType' => 'application/vnd.joomla.item.v1; schema=articles.v1',
27 | 'describedBy' => 'http://docs.joomla.org/Schemas/articles/v1',
28 | 'primaryRel' => 'joomla:articles',
29 | 'resourceMap' => __DIR__ . '/resource.json',
30 | 'self' => '/joomla:articles/' . (int) $this->input->get('id'),
31 | 'tableName' => '#__content',
32 | );
33 |
34 | $this->setOptions($serviceOptions);
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/components/com_content/services/articles/list/embedded.json:
--------------------------------------------------------------------------------
1 | {
2 | "embedded":
3 | [
4 | "/access",
5 | "/featured",
6 | "/introText",
7 | "/language",
8 | "_links/self.href",
9 | "_links/joomla:assets.href",
10 | "_links/joomla:categories.href",
11 | "_links/joomla:checkout.href",
12 | "_links/joomla:checkout.method",
13 | "_links/joomla:introImage.href",
14 | "_links/joomla:introImage.float",
15 | "_links/joomla:introImage.title",
16 | "_links/joomla:introImage.caption",
17 | "metadata/authorName",
18 | "/ordering",
19 | "publish/alias",
20 | "publish/created",
21 | "publish/publishDown",
22 | "publish/publishUp",
23 | "/state",
24 | "/title"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/components/com_content/services/articles/list/get.php:
--------------------------------------------------------------------------------
1 | setDatabase();
23 |
24 | // Set the controller options.
25 | $serviceOptions = array(
26 | 'contentType' => 'application/vnd.joomla.list.v1',
27 | 'describedBy' => 'http://docs.joomla.org/Schemas/articles/v1',
28 | 'embeddedMap' => __DIR__ . '/embedded.json',
29 | 'primaryRel' => 'joomla:articles',
30 | 'resourceMap' => realpath(__DIR__ . '/../resource.json'),
31 | 'self' => '/joomla:articles',
32 | 'tableName' => '#__content',
33 | );
34 |
35 | $this->setOptions($serviceOptions);
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/components/com_content/services/articles/resource.json:
--------------------------------------------------------------------------------
1 | {
2 | "/access":"int:{access}",
3 | "/featured":"boolean:{featured}",
4 | "/fullText":"string:{fulltext}",
5 | "/introText":"string:{introtext}",
6 | "/language":"string:{language}",
7 | "_links/self.href":"string:/joomla:articles/{id}",
8 | "_links/joomla:assets.href":"string:/joomla:assets/{asset_id}",
9 | "_links/joomla:categories.href":"string:/joomla:categories/{catid}",
10 | "_links/joomla:checkin.href":"string:/joomla:articles/{id}?checkin",
11 | "_links/joomla:checkin.method":"string:post",
12 | "_links/joomla:checkout.href":"string:/joomla:articles/{id}?checkout",
13 | "_links/joomla:checkout.method":"string:post",
14 | "_links/joomla:createdBy.href":"string:/joomla:users/{created_by}",
15 | "_links/joomla:fullImage.href":"string:{images.image_fulltext}",
16 | "_links/joomla:fullImage.float":"float:{images.float_fulltext}",
17 | "_links/joomla:fullImage.title":"string:{images.image_fulltext_alt}",
18 | "_links/joomla:fullImage.caption":"string:{images.image_fulltext_caption}",
19 | "_links/joomla:introImage.href":"string:{images.image_intro}",
20 | "_links/joomla:introImage.float":"float:{images.float_intro}",
21 | "_links/joomla:introImage.title":"string:{images.image_intro_alt}",
22 | "_links/joomla:introImage.caption":"string:{images.image_intro_caption}",
23 | "_links/joomla:modifiedBy.href":"string:/joomla:users/{modified_by}",
24 | "_links/joomla:urlA.href":"string:{urls.urla}",
25 | "_links/joomla:urlA.title":"string:{urls.urlatext}",
26 | "_links/joomla:urlA.target":"target:{urls.targeta}",
27 | "_links/joomla:urlB.href":"string:{urls.urlb}",
28 | "_links/joomla:urlB.title":"string:{urls.urlbtext}",
29 | "_links/joomla:urlB.target":"target:{urls.targetb}",
30 | "_links/joomla:urlC.href":"string:{urls.urlc}",
31 | "_links/joomla:urlC.title":"string:{urls.urlctext}",
32 | "_links/joomla:urlC.target":"target:{urls.targetc}",
33 | "metadata/authorName":"string:{metadata.author}",
34 | "metadata/description":"string:{metadesc}",
35 | "metadata/keywords":"string:{metakey}",
36 | "metadata/robots":"string:{metadata.robots}",
37 | "metadata/rights":"string:{metadata.rights}",
38 | "metadata/xref":"string:{xreference}",
39 | "/ordering":"int:{ordering}",
40 | "publish/alias":"string:{alias}",
41 | "publish/created":"datetime:{created}",
42 | "publish/hits":"int:{hits}",
43 | "publish/publishDown":"datetime:{publish_down}",
44 | "publish/publishUp":"datetime:{publish_up}",
45 | "publish/revision":"int:{version}",
46 | "render/adminImagesLinksShow":"ynglobal:{attribs.show_urls_images_backend}",
47 | "render/adminOptionsShow":"ynglobal:{attribs.show_article_options}",
48 | "render/authorLink":"ynglobal:{attribs.link_author}",
49 | "render/authorShow":"ynglobal:{attribs.show_author}",
50 | "render/categoryLink":"ynglobal:{attribs.link_category}",
51 | "render/categoryParentLink":"ynglobal:{attribs.link_parent_category}",
52 | "render/categoryParentShow":"ynglobal:{attribs.show_parent_category}",
53 | "render/categoryShow":"ynglobal:{attribs.show_category}",
54 | "render/createdShow":"ynglobal:{attribs.show_create_date}",
55 | "render/hitsShow":"ynglobal:{attribs.show_hits}",
56 | "render/iconEmailShow":"ynglobal:{attribs.show_email_icon}",
57 | "render/iconPrintShow":"ynglobal:{attribs.show_print_icon}",
58 | "render/iconsShow":"ynglobal:{attribs.show_icons}",
59 | "render/introShow":"ynglobal:{attribs.show_intro}",
60 | "render/linksPosition":"position:{attribs.info_block_position}",
61 | "render/modifiedShow":"ynglobal:{attribs.show_modify_date}",
62 | "render/navigationShow":"ynglobal:{attribs.show_item_navigation}",
63 | "render/noauthLinksShow":"ynglobal:{attribs.show_noauth}",
64 | "render/publishShow":"ynglobal:{attribs.show_publish_date}",
65 | "render/readMore":"string:{attribs.alternative_readmore}",
66 | "render/siteImagesLinksShow":"ynglobal:{attribs.show_item_navigation}",
67 | "render/siteOptionsShow":"ynglobal:{attribs.show_article_options}",
68 | "render/titleLink":"ynglobal:{attribs.link_titles}",
69 | "render/titleShow":"ynglobal:{attribs.show_title}",
70 | "render/votingShow":"ynglobal:{attribs.show_vote}",
71 | "/state":"state:{state}",
72 | "/title":"string:{title}"
73 | }
74 |
--------------------------------------------------------------------------------
/components/com_content/services/articles/transform/position.php:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/components/com_weblinks/services.json:
--------------------------------------------------------------------------------
1 | {
2 | "joomla:weblinks":"component/weblinks/WeblinksList",
3 | "joomla:weblinks/:id":"component/weblinks/Weblinks",
4 | "joomla:categories/:catid/weblinks":"component/weblinks/WeblinksList"
5 | }
6 |
--------------------------------------------------------------------------------
/components/com_weblinks/services/index.html:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/components/com_weblinks/services/weblinks/get.php:
--------------------------------------------------------------------------------
1 | setDatabase();
23 |
24 | // Set the controller options.
25 | $serviceOptions = array(
26 | 'contentType' => 'application/vnd.joomla.item.v1; schema=weblinks.v1',
27 | 'describedBy' => 'http://docs.joomla.org/Schemas/weblinks/v1',
28 | 'primaryRel' => 'joomla:weblinks',
29 | 'resourceMap' => __DIR__ . '/resource.json',
30 | 'self' => '/joomla:weblinks/' . (int) $this->input->get('id'),
31 | 'tableName' => '#__weblinks',
32 | );
33 |
34 | $this->setOptions($serviceOptions);
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/components/com_weblinks/services/weblinks/list/create.php:
--------------------------------------------------------------------------------
1 | setDatabase();
23 |
24 | // Set the controller options.
25 | $serviceOptions = array(
26 | // 'contentType' => 'application/vnd.joomla.list.v1',
27 | // 'describedBy' => 'http://docs.joomla.org/Schemas/weblinks/v1',
28 | // 'embeddedMap' => __DIR__ . '/embedded.json',
29 | // 'primaryRel' => 'joomla:weblinks',
30 | 'resourceMap' => __DIR__ . '/../resource.json',
31 | // 'self' => '/joomla:weblinks',
32 | // 'tableName' => '#__weblinks',
33 | );
34 |
35 | $this->setOptions($serviceOptions);
36 | }
37 |
38 | /**
39 | * Execute the request.
40 | */
41 | public function execute()
42 | {
43 | print_r($this->app->input);
44 |
45 | $array = array(
46 | '_meta' => array(
47 | 'apiVersion' => 'string',
48 | 'contentType' => 'string',
49 | 'describedBy' => 'string',
50 | ),
51 | 'description' => 'string',
52 | );
53 | $json = $this->app->input->getArray($array);
54 | print_r($json);
55 |
56 | // Get service object.
57 | $service = $this->getService();
58 |
59 | // Get the resource map.
60 | $resourceMap = $service->getResourceMap();
61 |
62 | print_r($resourceMap);
63 |
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/components/com_weblinks/services/weblinks/list/embedded.json:
--------------------------------------------------------------------------------
1 | {
2 | "embedded":
3 | [
4 | "/access",
5 | "/featured",
6 | "/language",
7 | "_links/self.href",
8 | "_links/joomla:categories.href",
9 | "_links/joomla:checkout.href",
10 | "_links/joomla:checkout.method",
11 | "metadata/authorName",
12 | "/ordering",
13 | "publish/alias",
14 | "publish/created",
15 | "publish/publishDown",
16 | "publish/publishUp",
17 | "/state",
18 | "/title"
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/components/com_weblinks/services/weblinks/list/get.php:
--------------------------------------------------------------------------------
1 | setDatabase();
23 |
24 | // Set the controller options.
25 | $serviceOptions = array(
26 | 'contentType' => 'application/vnd.joomla.list.v1',
27 | 'describedBy' => 'http://docs.joomla.org/Schemas/weblinks/v1',
28 | 'embeddedMap' => __DIR__ . '/embedded.json',
29 | 'primaryRel' => 'joomla:weblinks',
30 | 'resourceMap' => realpath(__DIR__ . '/../resource.json'),
31 | 'self' => '/joomla:weblinks',
32 | 'tableName' => '#__weblinks',
33 | );
34 |
35 | $this->setOptions($serviceOptions);
36 | }
37 |
38 | }
39 |
--------------------------------------------------------------------------------
/components/com_weblinks/services/weblinks/resource.json:
--------------------------------------------------------------------------------
1 | {
2 | "/access":"int:{access}",
3 | "/description":"string:{description}",
4 | "/featured":"boolean:{featured}",
5 | "/language":"string:{language}",
6 | "_links/self.href":"string:/joomla:weblinks/{id}",
7 | "_links/joomla:categories.href":"string:/joomla:categories/{catid}",
8 | "_links/joomla:checkin.href":"string:/joomla:weblinks/{id}?checkin",
9 | "_links/joomla:checkin.method":"string:post",
10 | "_links/joomla:checkout.href":"string:/joomla:weblinks/{id}?checkout",
11 | "_links/joomla:checkout.method":"string:post",
12 | "_links/joomla:createdBy.href":"string:/joomla:users/{created_by}",
13 | "_links/joomla:image1.href":"string:{images.image_first}",
14 | "_links/joomla:image1.float":"float:{images.float_first}",
15 | "_links/joomla:image1.title":"string:{images.image_first_alt}",
16 | "_links/joomla:image1.caption":"string:{images.image_first_caption}",
17 | "_links/joomla:image2.href":"string:{images.image_second}",
18 | "_links/joomla:image2.float":"float:{images.float_second}",
19 | "_links/joomla:image2.title":"string:{images.image_second_alt}",
20 | "_links/joomla:image2.caption":"string:{images.image_second_caption}",
21 | "_links/joomla:modifiedBy.href":"string:/joomla:users/{modified_by}",
22 | "metadata/authorName":"string:{created_by_alias}",
23 | "metadata/description":"string:{metadesc}",
24 | "metadata/keywords":"string:{metakey}",
25 | "metadata/robots":"string:{metadata.robots}",
26 | "metadata/rights":"string:{metadata.rights}",
27 | "metadata/xref":"string:{xreference}",
28 | "/ordering":"int:{ordering}",
29 | "publish/alias":"string:{alias}",
30 | "publish/created":"datetime:{created}",
31 | "publish/hits":"int:{hits}",
32 | "publish/publishDown":"datetime:{publish_down}",
33 | "publish/publishUp":"datetime:{publish_up}",
34 | "publish/revision":"int:{version}",
35 | "render/countClicks":"ynglobal:{params.count_clicks}",
36 | "render/height":"int:{params.height}",
37 | "render/target":"target:{params.target}",
38 | "render/width":"int:{params.width}",
39 | "/state":"state:{state}",
40 | "/title":"string:{title}",
41 | "/url":"string:{url}"
42 | }
43 |
--------------------------------------------------------------------------------
/etc/README.md:
--------------------------------------------------------------------------------
1 | /etc
2 | ====
3 | Here you will find a couple of JSON files used for configuration of the API.
4 |
5 | Note that the API code also grabs configuration data from the regular Joomla configuration.php file.
6 |
7 | ## config.json
8 | Contains general configuration data.
9 |
10 | ### absoluteHrefs
11 | Set to true if link relation hrefs should be rendered as absolute instead of relative URLs. Default is false.
12 | If you want to use the HAL Browser then you will need to set this to true.
13 |
14 | ## services.json
15 | Contains routes for services that are not provided by Joomla components. Not currently used and contains dummy data.
16 |
17 |
--------------------------------------------------------------------------------
/etc/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "debug": false,
3 | "absoluteHrefs": true
4 | }
5 |
--------------------------------------------------------------------------------
/etc/services.json:
--------------------------------------------------------------------------------
1 | {
2 | "joomla:categories":"CategoriesList",
3 | "joomla:categories/:id":"Categories",
4 | "joomla:menuitems":"MenuitemsList",
5 | "joomla:menuitems/:id":"Menuitems"
6 | }
7 |
--------------------------------------------------------------------------------