summaryrefslogtreecommitdiffstats
path: root/vendor/fguillot/picofeed/docs/config.markdown
blob: 75546abd162cb86d8ad7dc9a3256a27bdc612fce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
Configuration
=============

How to use the Config object
----------------------------

To change the default parameters, you have to use the Config class.
Create a new instance and pass it to the Reader object like that:

```php
use PicoFeed\Reader\Reader;
use PicoFeed\Config\Config;

$config = new Config;
$config->setClientUserAgent('My custom RSS Reader')
       ->setProxyHostname('127.0.0.1')
       ->setProxyPort(8118);

$reader = new Reader($config);
...
```

HTTP Client parameters
----------------------

### Connection timeout

- Method name: `setClientTimeout()`
- Default value: 10 seconds
- Argument value: number of seconds (integer)

```php
$config->setClientTimeout(20); // 20 seconds
```

### User Agent

- Method name: `setClientUserAgent()`
- Default value: `PicoFeed (https://github.com/fguillot/picoFeed)`
- Argument value: string

```php
$config->setClientUserAgent('My RSS reader');
```

### Maximum HTTP redirections

- Method name: `setMaxRedirections()`
- Default value: 5
- Argument value: integer

```php
$config->setMaxRedirections(10);
```

### Maximum HTTP body response size

- Method name: `setMaxBodySize()`
- Default value: 2097152 (2MB)
- Argument value: value in bytes (integer)

```php
$config->setMaxBodySize(10485760); // 10MB
```

### Proxy hostname

- Method name: `setProxyHostname()`
- Default value: empty
- Argument value: string

```php
$config->setProxyHostname('proxy.example.org');
```

### Proxy port

- Method name: `setProxyPort()`
- Default value: 3128
- Argument value: port number (integer)

```php
$config->setProxyPort(8118);
```

### Proxy username

- Method name: `setProxyUsername()`
- Default value: empty
- Argument value: string

```php
$config->setProxyUsername('myuser');
```

### Proxy password

- Method name: `setProxyPassword()`
- Default value: empty
- Argument value: string

```php
$config->setProxyPassword('mysecret');
```

Content grabber
---------------

### Connection timeout

- Method name: `setGrabberTimeout()`
- Default value: 10 seconds
- Argument value: number of seconds (integer)

```php
$config->setGrabberTimeout(20); // 20 seconds
```

### User Agent

- Method name: `setGrabberUserAgent()`
- Default value: `PicoFeed (https://github.com/fguillot/picoFeed)`
- Argument value: string

```php
$config->setGrabberUserAgent('My content scraper');
```

Parser
------

### Hash algorithm used for item id generation

- Method name: `setParserHashAlgo()`
- Default value: `sha256`
- Argument value: any value returned by the function `hash_algos()` (string)
- See: http://php.net/hash_algos

```php
$config->setParserHashAlgo('sha1');
```

### Disable item content filtering

- Method name: `setContentFiltering()`
- Default value: true (filtering is enabled by default)
- Argument value: boolean

```php
$config->setContentFiltering(false);
```

### Timezone

- Method name: `setTimezone()`
- Default value: UTC
- Argument value: See https://php.net/manual/en/timezones.php (string)
- Note: define the timezone for items/feeds

```php
$config->setTimezone('Europe/Paris');
```

Logging
-------

### Timezone

- Method name: `setTimezone()`
- Default value: UTC
- Argument value: See https://php.net/manual/en/timezones.php (string)
- Note: define the timezone for the logging class

```php
$config->setTimezone('Europe/Paris');
```

Filter
------

### Set the iframe whitelist (allowed iframe sources)

- Method name: `setFilterIframeWhitelist()`
- Default value: See the Filter class source code
- Argument value: array

```php
$config->setFilterIframeWhitelist(['http://www.youtube.com', 'http://www.vimeo.com']);
```

### Define HTML integer attributes

- Method name: `setFilterIntegerAttributes()`
- Default value: See the Filter class source code
- Argument value: array

```php
$config->setFilterIntegerAttributes(['width', 'height']);
```

### Add HTML attributes automatically

- Method name: `setFilterAttributeOverrides()`
- Default value: See the Filter class source code
- Argument value: array

```php
$config->setFilterAttributeOverrides(['a' => ['target' => '_blank']);
```

### Set the list of required attributes for tags

- Method name: `setFilterRequiredAttributes()`
- Default value: See the Filter class source code
- Argument value: array
- Note: If the required attributes are not there, the tag is stripped

```php
$config->setFilterRequiredAttributes(['a' => 'href', 'img' => 'src']);
```

### Set the resource blacklist (Ads blocker)

- Method name: `setFilterMediaBlacklist()`
- Default value: See the Filter class source code
- Argument value: array
- Note: Tags are stripped if they have those URLs

```php
$config->setFilterMediaBlacklist(['feeds.feedburner.com', 'share.feedsportal.com']);
```

### Define which attributes are used for external resources

- Method name: `setFilterMediaAttributes()`
- Default value: See the Filter class source code
- Argument value: array

```php
$config->setFilterMediaAttributes(['src', 'href']);
```

### Define the scheme whitelist

- Method name: `setFilterSchemeWhitelist()`
- Default value: See the Filter class source code
- Argument value: array
- See: http://en.wikipedia.org/wiki/URI_scheme

```php
$config->setFilterSchemeWhitelist(['http://', 'ftp://']);
```

### Define the tags and attributes whitelist

- Method name: `setFilterWhitelistedTags()`
- Default value: See the Filter class source code
- Argument value: array
- Note: Only those tags are allowed everything else is stripped

```php
$config->setFilterWhitelistedTags(['a' => ['href'], 'img' => ['src', 'title']]);
```

### Define a image proxy url

- Method name: `setFilterImageProxyUrl()`
- Default value: Empty
- Argument value: string

```php
$config->setFilterImageProxyUrl('http://myproxy.example.org/?url=%s');
```

### Define a image proxy callback

- Method name: `setFilterImageProxyCallback()`
- Default value: null
- Argument value: Closure

```php
$config->setFilterImageProxyCallback(function ($image_url) {
    $key = hash_hmac('sha1', $image_url, 'secret');
    return 'https://mypublicproxy/'.$key.'/'.urlencode($image_url);
});
```
6699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
# External API v1-2 (Legacy)

The **News app 1.2** offers a RESTful API

## API stability contract

The API level will **change** if the following occurs:

* A field of an object is removed
* A field of an object has a different datatype
* The meaning of an API call changes

The API level will **not change** if:

* The app version is changed (e.g. 4.0.1.2 instead of 4.0 or 4.001)
* A new attribute is added (e.g. each item gets a new field "something": 1)
* The order of the JSON attributes is changed on any level (e.g. "id":3 is not the first field anymore, but the last)

You have to design your app with these things in mind!:

* **Don't depend on the order of object attributes. In JSON it does not matter where the object attribute is since you access the value by name, not by index**
* **Don't limit your app to the currently available attributes. New ones might be added. If you don't handle them, ignore them**
* **Use a library to compare versions, ideally one that uses semantic versioning**

## Authentication & Basics
Because REST is stateless you have to send user and password each time you access the API. Therefore running Nextcloud **with SSL is highly recommended** otherwise **everyone in your network can log your credentials**.

The base URL for all calls is:

    https://yournextcloud.com/index.php/apps/news/api/v1-2/

All defined routes in the Specification are appended to this url. To access all feeds for instance use this url:

    https://yournextcloud.com/index.php/apps/news/api/v1-2/feeds

Credentials need to be passed as an HTTP header using [HTTP basic auth](https://en.wikipedia.org/wiki/Basic_access_authentication#Client_side):

    Authorization: Basic $CREDENTIALS

where $CREDENTIALS is:

    base64(USER:PASSWORD)

## How To Sync
This is a small overview over how you should sync your articles with the Nextcloud News app. For more fine-grained details about the API see further down.

All routes are given relative to the base API url (e.g.: https://yournextcloud.com/index.php/apps/news/api/v1-2)

### Initial Sync
The intial sync happens, when a user adds an Nextcloud account in your app. In that case you should fetch all feeds, folders and unread or starred articles from the News app. Do not fetch all articles, not only because it syncs faster, but also because the user is primarily interested in unread articles. To fetch all unread and starred articles, you must call 4 routes:

* **unread articles**: GET /items?type=3&getRead=false&batchSize=-1
* **starred articles**: GET /items?type=2&getRead=true&batchSize=-1
* **folders**: GET /folders
* **feeds**: GET /feeds

The JSON response structures can be viewed further down.

### Syncing
When syncing, you want to push read/unread and starred/unstarred items to the server and receive new and updated items, feeds and folders. To do that, call the following routes:

* **Notify the News app of unread articles**: PUT /items/unread/multiple {"items": [1, 3, 5] }
* **Notify the News app of read articles**: PUT /items/read/multiple {"items": [1, 3, 5]}
* **Notify the News app of starred articles**: PUT /items/starred/multiple {"items": [{"feedId": 3, "guidHash": "adadafasdasd1231"}, ...]}
* **Notify the News app of unstarred articles**: PUT /items/unstarred/multiple {"items": [{"feedId": 3, "guidHash": "adadafasdasd1231"}, ...]}
* **Get new folders**: GET /folders
* **Get new feeds**: GET /feeds
* **Get new items and modified items**: GET /items/updated?lastModified=12123123123&type=3


## Accessing API from a web application

**News 1.401** implements CORS which allows web applications to access the API. **To access the API in a webapp you need to send the correct authorization header instead of simply putting auth data into the URL!**. An example request in jQuery would look like this:

```js
$.ajax({
	type: 'GET',
	url: 'https://yournextcloud.com/index.php/apps/news/api/v1-2/version',
	contentType: 'application/json',
	success: function (response) {
		// handle success
	},
	error: function () {
		// handle errors
	},
	beforeSend: function (xhr) {
		var username = 'john';
		var password = 'doe';
		var auth = btoa(username + ':' + password);
		xhr.setRequestHeader('Authorization', 'Basic ' + auth);
	}
});
```
An example with AngularJS would look like this:
```js
angular.module('YourApp', [])
    .config(['$httpProvider', '$provide', function ($httpProvider, $provide) {
        $provide.factory('AuthInterceptor', ['Credentials', '$q', function (Credentials, $q) {
            return {
                request: function (config) {
                    // only set auth headers if url matches the api url
                    if(config.url.indexOf(Credentials.url) === 0) {
                        auth = btoa(Credentials.userName + ':' + Credentials.password);
                        config.headers['Authorization'] = 'Basic ' + auth;
                    }
                    return config || $q.when(config);
                }
            };
        }]);
        $httpProvider.interceptors.push('AuthInterceptor');
    }])
    .factory('Credentials', function () {
        return {
            userName: 'user',
            password: 'password',
            url: 'https://yournextcloud.com/index.php/apps/news/api'
        };
    })
    .run(['$http', function($http) {
        $http({
            method: 'GET',
            url: 'https://yournextcloud.com/index.php/apps/news/api/v1-2/version'
        }).success(function (data, status, header, config) {
            // handle success
        }).error(function (data, status, header, config) {
            // handle error
        });
    }]);
```

## Input
In general the input parameters can be in the URL or request body, the App Framework doesnt differentiate between them.

So JSON in the request body like:
```js
{
  "id": 3
}
```
will be treated the same as

    /?id=3

It is recommended though that you use the following convention:

* **GET**: parameters in the URL
* **POST**: parameters as JSON in the request body
* **PUT**: parameters as JSON in the request body
* **DELETE**: parameters as JSON in the request body

## Output
The output is JSON.

# Folders
## Get all folders

* **Status**: Implemented
* **Method**: GET
* **Route**: /folders
* **Parameters**: none
* **Returns**:
```js
{
  "folders": [
    {
      "id": 4,
      "name": "Media"
    }, // etc
  ]
}
```

## Create a folder
Creates a new folder and returns a new folder object

* **Status**: Implemented
* **Method**: POST
* **Route**: /folders
* **Parameters**:
```js
{
  "name": "folder name"
}
```
* **Return codes**:
 * **HTTP 409**: If the folder exists already
 * **HTTP 422**: If the folder name is invalid (for instance empty)
* **Returns**:
```js
{
  "folders": [
    {
      "id": 4,
      "name": "Media"
    }
  ]
}
```

## Delete a folder
Deletes a folder with the id folderId and all the feeds it contains

* **Status**: Implemented
* **Method**: DELETE
* **Route**: /folders/{folderId}
* **Parameters**: none
* **Return codes**:
 * **HTTP 404**: If the folder does not exist
* **Returns**: nothing

## Rename a folder
Only the name can be updated

* **Status**: Implemented
* **Method**: PUT
* **Route**: /folders/{folderId}
* **Parameters**:
```js
{
  "name": "folder name"
}
```
* **Return codes**:
 * **HTTP 409**: If the folder name does already exist
 * **HTTP 404**: If the folder does not exist
 * **HTTP 422**: If the folder name is invalid (for instance empty)
* **Returns**: nothing

## Mark items of a folder as read

* **Status**: Implemented
* **Method**: PUT
* **Route**: /folders/{folderId}/read
* **Parameters**:
```js
{
    // mark all items read lower than equal that id
    // this is mean to prevent marking items as read which the client/user does not yet know of
    "newestItemId": 10
}
```
* **Return codes**:
 * **HTTP 404**: If the feed does not exist
* **Returns**: nothing

# Feeds

## Sanitation

The following attributes are **not sanitized** meaning: including them in your web application can lead to  XSS:

* **title**
* **link**

## Get all feeds

* **Status**: Implemented
* **Method**: GET
* **Route**: /feeds
* **Parameters**: none
* **Returns**:
```js
{
  "feeds": [
    {