Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/news.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'search/search_index.json')
-rw-r--r--search/search_index.json1
1 files changed, 1 insertions, 0 deletions
diff --git a/search/search_index.json b/search/search_index.json
new file mode 100644
index 000000000..68f2591b3
--- /dev/null
+++ b/search/search_index.json
@@ -0,0 +1 @@
+{"config":{"indexing":"full","lang":["en"],"min_search_length":3,"prebuild_index":false,"separator":"[\\s\\-]+"},"docs":[{"location":"","text":"Introduction Welcome to the Nextcloud News APP documentation, it contains information for users, administrators and developers. News is an APP for Nextcloud that can be installed from the official APP Store . News offers the user an RSS/Atom feed reader and can be used to subscribe to multiple feeds, which get automatically updated in the background. Additionally, news offers a REST-API that allows clients to synchronize with News.","title":"Introduction"},{"location":"#introduction","text":"Welcome to the Nextcloud News APP documentation, it contains information for users, administrators and developers. News is an APP for Nextcloud that can be installed from the official APP Store . News offers the user an RSS/Atom feed reader and can be used to subscribe to multiple feeds, which get automatically updated in the background. Additionally, news offers a REST-API that allows clients to synchronize with News.","title":"Introduction"},{"location":"admin/","text":"Admin Welcome to the Admin documentation this page explains some of the configuration options for news. System Cron Nextcloud uses cron to run regular jobs, News relies on the Job system to execute the feed updates. Alternatively you may use an external updater , in this case you need to disable the system cron in the settings. Auto purge count The default value is 200. To disable this feature, use -1. Unread and starred items are not deleted. Auto purging automatically removes the oldest read items of every feed after every update. The value you enter here is used as the limit of read items per feed, unless the feed comes with more items in it's feed. For example you have the default value of 200 and the feed has 210 items in it's feed. In this case the limit will be 210 instead of 200. Purge unread items This changes the behavior of the auto purging to also purge unread items. This is useful if you have users with a lot of unread items. Starred items are always kept. Explore Service If you are using the News app in your company/community, it might be interesting to offer your users a bunch of easily to discover default feeds. You could also create a website where people can add and up-vote news feeds like bigger cloud feed readers like Feedly do it or even convert their APIs into a service for the News app (if someone wants to provide one for the News app, feel free to contact us by creating an issue in the bug tracker). The URL should be a path to a directory which contains a JSON file in the format of feeds.LANG_CODE.json where LANG_CODE is a two character language code (e.g. en or de ). For example, entering the URL https://domain.com/directory as explore URL will produce the following request for German users: GET https://domain.com/directory/feeds.de.json Do not forget to implement CORS in your API, otherwise the request will fail! Update Interval The update interval is used to determine when the next update of all feeds should be done. By default, the value is set to 3600 seconds (1 hour) You can configure this interval as an administrator. The new value is only applied after the next run of the updater. What is a good update interval? That depends on your individual needs. Please keep in mind that the lower you set your update interval, the more traffic is generated. Can I set individual update intervals per feed/user? No, that is not possible.","title":"Admin"},{"location":"admin/#admin","text":"Welcome to the Admin documentation this page explains some of the configuration options for news.","title":"Admin"},{"location":"admin/#system-cron","text":"Nextcloud uses cron to run regular jobs, News relies on the Job system to execute the feed updates. Alternatively you may use an external updater , in this case you need to disable the system cron in the settings.","title":"System Cron"},{"location":"admin/#auto-purge-count","text":"The default value is 200. To disable this feature, use -1. Unread and starred items are not deleted. Auto purging automatically removes the oldest read items of every feed after every update. The value you enter here is used as the limit of read items per feed, unless the feed comes with more items in it's feed. For example you have the default value of 200 and the feed has 210 items in it's feed. In this case the limit will be 210 instead of 200.","title":"Auto purge count"},{"location":"admin/#purge-unread-items","text":"This changes the behavior of the auto purging to also purge unread items. This is useful if you have users with a lot of unread items. Starred items are always kept.","title":"Purge unread items"},{"location":"admin/#explore-service","text":"If you are using the News app in your company/community, it might be interesting to offer your users a bunch of easily to discover default feeds. You could also create a website where people can add and up-vote news feeds like bigger cloud feed readers like Feedly do it or even convert their APIs into a service for the News app (if someone wants to provide one for the News app, feel free to contact us by creating an issue in the bug tracker). The URL should be a path to a directory which contains a JSON file in the format of feeds.LANG_CODE.json where LANG_CODE is a two character language code (e.g. en or de ). For example, entering the URL https://domain.com/directory as explore URL will produce the following request for German users: GET https://domain.com/directory/feeds.de.json Do not forget to implement CORS in your API, otherwise the request will fail!","title":"Explore Service"},{"location":"admin/#update-interval","text":"The update interval is used to determine when the next update of all feeds should be done. By default, the value is set to 3600 seconds (1 hour) You can configure this interval as an administrator. The new value is only applied after the next run of the updater.","title":"Update Interval"},{"location":"admin/#what-is-a-good-update-interval","text":"That depends on your individual needs. Please keep in mind that the lower you set your update interval, the more traffic is generated.","title":"What is a good update interval?"},{"location":"admin/#can-i-set-individual-update-intervals-per-feeduser","text":"No, that is not possible.","title":"Can I set individual update intervals per feed/user?"},{"location":"clients/","text":"Clients Clients are applications that use the REST API of News. They are not maintained by the News team. If you are the developer of an app, feel free to create a PR to add your app to this list. Sync Clients A sync client can be used to read news and synchronize via the API. Name OS/Platform License Source RSS Guard Windows, Linux, OS/2, macOS GPL-3.0 License GitHub Fluent Reader Windows, Linux, macOS BSD-3-Clause License GitHub Communique Linux LGPL-2.1 License GitHub Nextcloud News Reader Android GPL-3.0 License GitHub OCReader Android GPL-3.0 License GitHub Newsout Android Apache-2.0 License GitHub Readrops Android GPL-3.0 License GitHub CloudNews iOS BSD-2-Clause License GitHub Fiery Feeds iOS, macOS - - Nextnews iOS - - News Checker Google Chrome - GitHub own News Blackberry - - FeedSpider Firefox OS, webOS, LuneOS MIT License GitHub fastReader Windows Phone - - py3status i3wm BSD-3-Clause License GitHub newsboat Unix Terminal MIT License GitHub Newsie Ubuntu Touch GPL-3.0 License GitLab Fuoten Sailfish OS GPL-3.0 License GitHub Update Clients An update client uses the News API or the OCC CLI to update the feeds in News. Python multithread updater","title":"Clients"},{"location":"clients/#clients","text":"Clients are applications that use the REST API of News. They are not maintained by the News team. If you are the developer of an app, feel free to create a PR to add your app to this list.","title":"Clients"},{"location":"clients/#sync-clients","text":"A sync client can be used to read news and synchronize via the API. Name OS/Platform License Source RSS Guard Windows, Linux, OS/2, macOS GPL-3.0 License GitHub Fluent Reader Windows, Linux, macOS BSD-3-Clause License GitHub Communique Linux LGPL-2.1 License GitHub Nextcloud News Reader Android GPL-3.0 License GitHub OCReader Android GPL-3.0 License GitHub Newsout Android Apache-2.0 License GitHub Readrops Android GPL-3.0 License GitHub CloudNews iOS BSD-2-Clause License GitHub Fiery Feeds iOS, macOS - - Nextnews iOS - - News Checker Google Chrome - GitHub own News Blackberry - - FeedSpider Firefox OS, webOS, LuneOS MIT License GitHub fastReader Windows Phone - - py3status i3wm BSD-3-Clause License GitHub newsboat Unix Terminal MIT License GitHub Newsie Ubuntu Touch GPL-3.0 License GitLab Fuoten Sailfish OS GPL-3.0 License GitHub","title":"Sync Clients"},{"location":"clients/#update-clients","text":"An update client uses the News API or the OCC CLI to update the feeds in News. Python multithread updater","title":"Update Clients"},{"location":"developer/","text":"Developer Welcome to the Nextcloud News App developer documentation. News is open for contributions, if you plan to implement a new feature make sure to open a discussion . Describe the feature that you are planing and your first idea how to implement it. This ensures that you don't start working on something which collides with the targets of the maintainers. For small fixes and improvements feel free to directly create a PR, the maintainers are happy to review your code. APIs News offers an API that can be used by clients to synchronize with the server. There are two API declarations, so far only V1 has been fully implemented. Work on V2 has started with low priority. API-V1.2 API-V1.3 API-V2 Coding Style Guidelines The PHP code should all adhere to PSR-2 . Note that this is a different codestyle than Nextcloud itself uses. To test the codestyle you can run make phpcs . For linting JavaScript, a jshint file is used that is run before compiling the JavaScript. General Developer setup Check the Nextcloud documentation to learn how to setup a developer environment, alternatively to a proper web server you can also use the builtin php server on demand, it is enough for development purposes. When your setup is running, clone the news repository in the apps/ directory inside the server. Change into the news directory and run make to build the app, you will need php, composer, node, npm and maybe more. Now you can basically use the news app and test your changes. Running Integration tests locally We use bats to run integration tests against the API and the cli. Check how to install bats on your system in the official documentation . You also need to pull the submodules of the news repo. git submodules update --init The cli tests expect that the feeds are reachable at http://localhost:8090 , to achieve that you can use make feed-server & the & means it'll run in the background. Now the test feeds will be reachable for bats. Run the tests by executing bats tests/command you can also only run specific tests for example bats tests/command/feeds.bats . For the API tests you need to run a second php server or have another web server that provides Nextcloud and the News App. The tests expect to find Nextcloud at http://localhost:8080 You can do this by running make nextcloud-server . The bats tests can be executed like this bats tests/api .","title":"Developer"},{"location":"developer/#developer","text":"Welcome to the Nextcloud News App developer documentation. News is open for contributions, if you plan to implement a new feature make sure to open a discussion . Describe the feature that you are planing and your first idea how to implement it. This ensures that you don't start working on something which collides with the targets of the maintainers. For small fixes and improvements feel free to directly create a PR, the maintainers are happy to review your code.","title":"Developer"},{"location":"developer/#apis","text":"News offers an API that can be used by clients to synchronize with the server. There are two API declarations, so far only V1 has been fully implemented. Work on V2 has started with low priority. API-V1.2 API-V1.3 API-V2","title":"APIs"},{"location":"developer/#coding-style-guidelines","text":"The PHP code should all adhere to PSR-2 . Note that this is a different codestyle than Nextcloud itself uses. To test the codestyle you can run make phpcs . For linting JavaScript, a jshint file is used that is run before compiling the JavaScript.","title":"Coding Style Guidelines"},{"location":"developer/#general-developer-setup","text":"Check the Nextcloud documentation to learn how to setup a developer environment, alternatively to a proper web server you can also use the builtin php server on demand, it is enough for development purposes. When your setup is running, clone the news repository in the apps/ directory inside the server. Change into the news directory and run make to build the app, you will need php, composer, node, npm and maybe more. Now you can basically use the news app and test your changes.","title":"General Developer setup"},{"location":"developer/#running-integration-tests-locally","text":"We use bats to run integration tests against the API and the cli. Check how to install bats on your system in the official documentation . You also need to pull the submodules of the news repo. git submodules update --init The cli tests expect that the feeds are reachable at http://localhost:8090 , to achieve that you can use make feed-server & the & means it'll run in the background. Now the test feeds will be reachable for bats. Run the tests by executing bats tests/command you can also only run specific tests for example bats tests/command/feeds.bats . For the API tests you need to run a second php server or have another web server that provides Nextcloud and the News App. The tests expect to find Nextcloud at http://localhost:8080 You can do this by running make nextcloud-server . The bats tests can be executed like this bats tests/api .","title":"Running Integration tests locally"},{"location":"faq/","text":"FAQ My browser shows a mixed content warning (Connection is Not Secure) If you are serving your Nextcloud over HTTPS your browser will very likely warn you with a yellow warnings sign about your connection not being secure. Chrome will show no green HTTPS lock sign, Firefox will show you the following image Note that this warning is not red and won't block the page like the following images which signal a serious issue : What is the cause of the (yellow) error message This warning is caused by mixed passive content and means that your page loads passive resources from non HTTPS resources, such as: Images Video/Audio This allows a possible attacker to perform a MITM (man-in-the-middle) attack by serving you different images or audio/video. Why doesn't the News app fix it The News app fully prevents mixed active content by only allowing HTTPS iframes from known locations; other possible mixed active content elements such as <script> are stripped from the feed. Because images and audio/video are an integral part of a feed, we can not simply strip them. Since an attacker can not execute code in contrast to mixed active content, but only replace images/audio/video in your feed reader, this is not considered to be a security issue . If, for whatever reason (e.g. feed which would allow fishing), this is a security problem for you, contact the specific feed provider and ask him to serve his feed content over HTTPS. Why don't you simply use an HTTPS image/audio/video proxy For the same reason that we can't fix non HTTPS websites: It does not fix the underlying issue, but only silences it. If you are using an image HTTPS proxy, an attacker can simply attack your image proxy since the proxy fetches insecure content. Even worse : if your image proxy serves these images from the same domain as your Nextcloud installation, you are vulnerable to XSS via SVG images . In addition, people feel safe when essentially they are not. Since most people don't understand mixed content and don't have two domains and a standalone server for the image proxy, it is very likely they will choose to host it under the same domain. Because we care about our users' security and don't want to hide security warnings, we won't fix (aka silence) this issue. The only fix for this issue is that feed providers serve their content over HTTPS. I am getting: Exception: Some\\Class does not exist errors in my nextcloud.log This is very often caused by missing or old files, e.g. by failing to upload all the News app files or errors during installation. Before you report a bug, please recheck if all files from the archive are in place and accessible. Feeds not updated Feeds can be updated using Nextcloud's system cron or an external updater via the API The feed update is not run in Webcron and AJAX cron mode! System Cron Check if you are using the system cron (Cron) setting on the admin page. AJAX and Web cron will not update feeds Check if the cronjob exists with crontab -u www-data -e (replace www-data with your httpd user) Check the file permissions of the cron.php file and if www-data (or whatever your httpd user is called like) can read and execute that script Check if you can execute the cron with sudo -u www-data php -f nextcloud/cron.php (replace www-data with your httpd user) Check your data/nextcloud.log for errors Check if the cronjob is ever executed by placing an error_log('updating'); in the background job file . If the cronjob runs, there should be an updating log statement in your httpd log. If there is no updating statement in your logs check if your cronjob is executed by executing a different script Check if the oc_jobs table has a reserved_at entry with a value other than 0. If it does for whatever reason, set it to 0. You can check this by executing: sql SELECT * from oc_jobs WHERE class LIKE '%News%' ORDER BY id; You will get two rows where column class will be OCA\\News\\Cron\\Updater and OCA\\News\\Cron\\UpdaterJob . Reset the reserved_at by executing sql UPDATE oc_jobs SET reserved_at = 0 WHERE id = <id from above SELECT statement>; If your cron works fine, but Nextcloud's cronjobs are never executed, file a bug in server External Updater Check if your configuration is set to not use the system cron. Consult the documentation of the updater Check your data/nextcloud.log for errors Adding feeds that use self-signed certificates If you want to add a feed that uses a self-signed certificate that is not signed by a trusted CA the request will fail with \"SSL certificate is invalid\". A common solution is to turn off the certificate verification which is wrong and makes your installation vulnerable to MITM attacks . Therefore turning off certificate verification is not supported . If you have control over the feed in question, consider signing your certificate for free on one of the following providers: Let's Encrypt ZeroSSL If you do not have control over the chosen feed, you should download the certificate from the feed's website and add it to your server's trusted certificates . The exact procedure however may vary depending on your distribution. Is There An Subscription URL To Easily Subscribe To Feeds By appending ?subscribe_to=SOME_URL to your News app URL, you can launch the News app with a pre-filled URL, e.g.: https://yourdomain.com/nextcloud/index.php/apps/news?subscribe_to=https://github.com/nextcloud/news/releases Database table grows too big If your users have subscribed to some high-volume feeds where a lot of items remain unread, this can lead to an oversized news table over time. As a consequence, the database upgrade of the news app can take several hours, during which Nextcloud cannot be used. By default, Nextcloud News purges old news items above a certain threshold each time it fetches new news items. The maximum number of items per feed that should be kept during the purging can be defined through the \u201cMaximum read count per feed\u201d setting in the admin UI or the autoPurgeCount value in the config. Additionally you may enable the option to also purge unread items purgeUnread . This is useful if your users have large amounts of unread items. Starred items are always exempt from purging. The command occ news:updater:after-update [--purge-unread] [<purge-count>] can be used to manually purge old news items across the instance. With the --purge-unread option, unread items are also purged (starred items are still exempt). If purge-count is not specified, the configured autoPurgeCount is used. The purge count only applies to the items that are purged. For example, when purging a feed that has 100 unread items, 100 starred read items and 100 unstarred read items, using a purge-count of 50 would keep all unread and starred items and the latest 50 read items. Using a purge-count of 50 along with --purge-unread would keep the all starred items plus the latest 50 from the set of unread and read items. Missing 4-byte support SQLSTATE[22007]: Invalid datetime format: 1366 Incorrect string value: ... This is likely caused by your feed using emojis in the feed title or text. The DB is then not able to store the feed and runs into strange decoding errors. You need to convert your DB to support 4 bytes, check the Nextcloud documentation . References #1165 #526","title":"FAQ"},{"location":"faq/#faq","text":"","title":"FAQ"},{"location":"faq/#my-browser-shows-a-mixed-content-warning-connection-is-not-secure","text":"If you are serving your Nextcloud over HTTPS your browser will very likely warn you with a yellow warnings sign about your connection not being secure. Chrome will show no green HTTPS lock sign, Firefox will show you the following image Note that this warning is not red and won't block the page like the following images which signal a serious issue :","title":"My browser shows a mixed content warning (Connection is Not Secure)"},{"location":"faq/#what-is-the-cause-of-the-yellow-error-message","text":"This warning is caused by mixed passive content and means that your page loads passive resources from non HTTPS resources, such as: Images Video/Audio This allows a possible attacker to perform a MITM (man-in-the-middle) attack by serving you different images or audio/video.","title":"What is the cause of the (yellow) error message"},{"location":"faq/#why-doesnt-the-news-app-fix-it","text":"The News app fully prevents mixed active content by only allowing HTTPS iframes from known locations; other possible mixed active content elements such as <script> are stripped from the feed. Because images and audio/video are an integral part of a feed, we can not simply strip them. Since an attacker can not execute code in contrast to mixed active content, but only replace images/audio/video in your feed reader, this is not considered to be a security issue . If, for whatever reason (e.g. feed which would allow fishing), this is a security problem for you, contact the specific feed provider and ask him to serve his feed content over HTTPS.","title":"Why doesn't the News app fix it"},{"location":"faq/#why-dont-you-simply-use-an-https-imageaudiovideo-proxy","text":"For the same reason that we can't fix non HTTPS websites: It does not fix the underlying issue, but only silences it. If you are using an image HTTPS proxy, an attacker can simply attack your image proxy since the proxy fetches insecure content. Even worse : if your image proxy serves these images from the same domain as your Nextcloud installation, you are vulnerable to XSS via SVG images . In addition, people feel safe when essentially they are not. Since most people don't understand mixed content and don't have two domains and a standalone server for the image proxy, it is very likely they will choose to host it under the same domain. Because we care about our users' security and don't want to hide security warnings, we won't fix (aka silence) this issue. The only fix for this issue is that feed providers serve their content over HTTPS.","title":"Why don't you simply use an HTTPS image/audio/video proxy"},{"location":"faq/#i-am-getting-exception-someclass-does-not-exist-errors-in-my-nextcloudlog","text":"This is very often caused by missing or old files, e.g. by failing to upload all the News app files or errors during installation. Before you report a bug, please recheck if all files from the archive are in place and accessible.","title":"I am getting: Exception: Some\\Class does not exist errors in my nextcloud.log"},{"location":"faq/#feeds-not-updated","text":"Feeds can be updated using Nextcloud's system cron or an external updater via the API The feed update is not run in Webcron and AJAX cron mode!","title":"Feeds not updated"},{"location":"faq/#system-cron","text":"Check if you are using the system cron (Cron) setting on the admin page. AJAX and Web cron will not update feeds Check if the cronjob exists with crontab -u www-data -e (replace www-data with your httpd user) Check the file permissions of the cron.php file and if www-data (or whatever your httpd user is called like) can read and execute that script Check if you can execute the cron with sudo -u www-data php -f nextcloud/cron.php (replace www-data with your httpd user) Check your data/nextcloud.log for errors Check if the cronjob is ever executed by placing an error_log('updating'); in the background job file . If the cronjob runs, there should be an updating log statement in your httpd log. If there is no updating statement in your logs check if your cronjob is executed by executing a different script Check if the oc_jobs table has a reserved_at entry with a value other than 0. If it does for whatever reason, set it to 0. You can check this by executing: sql SELECT * from oc_jobs WHERE class LIKE '%News%' ORDER BY id; You will get two rows where column class will be OCA\\News\\Cron\\Updater and OCA\\News\\Cron\\UpdaterJob . Reset the reserved_at by executing sql UPDATE oc_jobs SET reserved_at = 0 WHERE id = <id from above SELECT statement>; If your cron works fine, but Nextcloud's cronjobs are never executed, file a bug in server","title":"System Cron"},{"location":"faq/#external-updater","text":"Check if your configuration is set to not use the system cron. Consult the documentation of the updater Check your data/nextcloud.log for errors","title":"External Updater"},{"location":"faq/#adding-feeds-that-use-self-signed-certificates","text":"If you want to add a feed that uses a self-signed certificate that is not signed by a trusted CA the request will fail with \"SSL certificate is invalid\". A common solution is to turn off the certificate verification which is wrong and makes your installation vulnerable to MITM attacks . Therefore turning off certificate verification is not supported . If you have control over the feed in question, consider signing your certificate for free on one of the following providers: Let's Encrypt ZeroSSL If you do not have control over the chosen feed, you should download the certificate from the feed's website and add it to your server's trusted certificates . The exact procedure however may vary depending on your distribution.","title":"Adding feeds that use self-signed certificates"},{"location":"faq/#is-there-an-subscription-url-to-easily-subscribe-to-feeds","text":"By appending ?subscribe_to=SOME_URL to your News app URL, you can launch the News app with a pre-filled URL, e.g.: https://yourdomain.com/nextcloud/index.php/apps/news?subscribe_to=https://github.com/nextcloud/news/releases","title":"Is There An Subscription URL To Easily Subscribe To Feeds"},{"location":"faq/#database-table-grows-too-big","text":"If your users have subscribed to some high-volume feeds where a lot of items remain unread, this can lead to an oversized news table over time. As a consequence, the database upgrade of the news app can take several hours, during which Nextcloud cannot be used. By default, Nextcloud News purges old news items above a certain threshold each time it fetches new news items. The maximum number of items per feed that should be kept during the purging can be defined through the \u201cMaximum read count per feed\u201d setting in the admin UI or the autoPurgeCount value in the config. Additionally you may enable the option to also purge unread items purgeUnread . This is useful if your users have large amounts of unread items. Starred items are always exempt from purging. The command occ news:updater:after-update [--purge-unread] [<purge-count>] can be used to manually purge old news items across the instance. With the --purge-unread option, unread items are also purged (starred items are still exempt). If purge-count is not specified, the configured autoPurgeCount is used. The purge count only applies to the items that are purged. For example, when purging a feed that has 100 unread items, 100 starred read items and 100 unstarred read items, using a purge-count of 50 would keep all unread and starred items and the latest 50 read items. Using a purge-count of 50 along with --purge-unread would keep the all starred items plus the latest 50 from the set of unread and read items.","title":"Database table grows too big"},{"location":"faq/#missing-4-byte-support-sqlstate22007-invalid-datetime-format-1366-incorrect-string-value","text":"This is likely caused by your feed using emojis in the feed title or text. The DB is then not able to store the feed and runs into strange decoding errors. You need to convert your DB to support 4 bytes, check the Nextcloud documentation . References #1165 #526","title":"Missing 4-byte support SQLSTATE[22007]: Invalid datetime format: 1366 Incorrect string value: ..."},{"location":"install/","text":"Installation/Update & Uninstall Dependencies 64bit OS (starting with News 16.0.0) PHP >= 7.3 Nextcloud 22 libxml >= 2.7.8 You also need some PHP extensions: json simplexml xml dom curl iconv Supported Databases PostgreSQL >= 10 MariaDB >= 10.2 MySQL >= 8.0 SQLite (discouraged) Also see the Nextcloud documentation . Oracle is currently not supported by news. Performance Notices Use MySQL/MariaDB or PostgreSQL for better database performance Use the updater script to thread and speed up the update Before you install/update the News app Before you install the app do the following: Check that your nextcloud/data/ directory is owned by your web server user and that it is write/readable Check that your installation fulfills the requirements listed above Set up Nextcloud Background Jobs to enable feed updates. Then proceed to install the app either from an archive (zip/tar.gz) or clone it from the repository using git Installing from the app store This is the easiest solution: Simply go the apps page (section: \"Multimedia\") and enable the News app Installing from archive Go to the Nextcloud News GitHub releases page and download the latest release/archive to your server The news.tar.gz file contains the compiled and signed app files, if you install from source you have to build the app on your own. On your server, check if there is a folder called nextcloud/apps/news . If there is one, delete it. Extract the downloaded archive to the nextcloud/apps/ folder. Remove the version from the extracted folder (e.g. rename nextcloud/apps/news-4.0.3/ to nextcloud/apps/news/ If you are a version greater than or equal to 8.0.0 and downloaded the Source code zip or tar.gz, you need to install the JavaScript and PHP dependencies and compile the JavaScript first. On your terminal, change into the nextcloud/apps/news/ directory and run the following command (requires node >5.6, npm, curl, make and which): sudo -u www-data make # www-data might vary depending on your distribution Finally make sure that the nextcloud/apps/news directory is owned by the web server user sudo chown -R www-data:www-data nextcloud/apps/news # www-data:www-data might vary depending on your distribution Activate the News app in the apps menu Installing from Git (development version) Build Dependencies These Dependencies are only relevant if you want to build the source code: make which Node.js >= 6 npm composer The master branch will always be stable in conjunction with the latest master branch from Nextcloud JavaScript and PHP libraries are not included anymore since 8.0.0 and will require you to run make after updating/installing the app In your terminal go into the nextcloud/apps/ directory and then run the following command: git clone https://github.com/nextcloud/news.git cd news make If you are using a stable Nextcloud release, stay with the latest git tag release which is running on your version . To get an overview over all existing tags run: git tag You can switch to a release which will be supported on your installation by running: git checkout tags/TAG make # if News version >= 8.0.0 For instance, to use the 5.2.8 release, run: git checkout tags/5.2.8 Activate the News app in the apps menu To update the News app use change into the nextcloud/apps/news/ directory using your terminal and then run: git pull --rebase origin master make Uninstall with cleanup First uninstall the app via the web-interface or via occ: ./occ app:remove news This currently does not remove any of the database tables. Data in your /tmp directory will be automatically deleted by the OS. If you changed the temporary directory for Nextcloud you need to check on your own. Careful, this next part is only intended for admins, that know what they are doing. To remove the tables from the DB we drop the tables of news. Your installation might have a different prefix then oc_ but it is the default in most installations. Connect to your DB and execute the commands. Don't forget to switch to the right database. For example in mysql: use nextcloud; DROP TABLE oc_news_folders; DROP TABLE oc_news_feeds; DROP TABLE oc_news_items; Then we remove the traces in the migrations table. DELETE FROM oc_migrations WHERE app='news'; Next delete the app configuration. DELETE FROM oc_appconfig WHERE appid = 'news'; And finally remove the jobs from the job table. The last two lines are only needed for older installations. DELETE FROM oc_jobs WHERE class='OCA\\\\News\\\\Cron\\\\UpdaterJob'; DELETE FROM oc_jobs WHERE class='OCA\\\\News\\\\Cron\\\\Updater'; DELETE FROM oc_jobs WHERE argument='[\"OCA\\\\\\\\News\\\\\\\\Cron\\\\\\\\Updater\",\"run\"]'; Now nothing is left from news in your nextcloud installation.","title":"Installation/Update & Uninstall"},{"location":"install/#installationupdate-uninstall","text":"","title":"Installation/Update &amp; Uninstall"},{"location":"install/#dependencies","text":"64bit OS (starting with News 16.0.0) PHP >= 7.3 Nextcloud 22 libxml >= 2.7.8 You also need some PHP extensions: json simplexml xml dom curl iconv","title":"Dependencies"},{"location":"install/#supported-databases","text":"PostgreSQL >= 10 MariaDB >= 10.2 MySQL >= 8.0 SQLite (discouraged) Also see the Nextcloud documentation . Oracle is currently not supported by news.","title":"Supported Databases"},{"location":"install/#performance-notices","text":"Use MySQL/MariaDB or PostgreSQL for better database performance Use the updater script to thread and speed up the update","title":"Performance Notices"},{"location":"install/#before-you-installupdate-the-news-app","text":"Before you install the app do the following: Check that your nextcloud/data/ directory is owned by your web server user and that it is write/readable Check that your installation fulfills the requirements listed above Set up Nextcloud Background Jobs to enable feed updates. Then proceed to install the app either from an archive (zip/tar.gz) or clone it from the repository using git","title":"Before you install/update the News app"},{"location":"install/#installing-from-the-app-store","text":"This is the easiest solution: Simply go the apps page (section: \"Multimedia\") and enable the News app","title":"Installing from the app store"},{"location":"install/#installing-from-archive","text":"Go to the Nextcloud News GitHub releases page and download the latest release/archive to your server The news.tar.gz file contains the compiled and signed app files, if you install from source you have to build the app on your own. On your server, check if there is a folder called nextcloud/apps/news . If there is one, delete it. Extract the downloaded archive to the nextcloud/apps/ folder. Remove the version from the extracted folder (e.g. rename nextcloud/apps/news-4.0.3/ to nextcloud/apps/news/ If you are a version greater than or equal to 8.0.0 and downloaded the Source code zip or tar.gz, you need to install the JavaScript and PHP dependencies and compile the JavaScript first. On your terminal, change into the nextcloud/apps/news/ directory and run the following command (requires node >5.6, npm, curl, make and which): sudo -u www-data make # www-data might vary depending on your distribution Finally make sure that the nextcloud/apps/news directory is owned by the web server user sudo chown -R www-data:www-data nextcloud/apps/news # www-data:www-data might vary depending on your distribution Activate the News app in the apps menu","title":"Installing from archive"},{"location":"install/#installing-from-git-development-version","text":"","title":"Installing from Git (development version)"},{"location":"install/#build-dependencies","text":"These Dependencies are only relevant if you want to build the source code: make which Node.js >= 6 npm composer The master branch will always be stable in conjunction with the latest master branch from Nextcloud JavaScript and PHP libraries are not included anymore since 8.0.0 and will require you to run make after updating/installing the app In your terminal go into the nextcloud/apps/ directory and then run the following command: git clone https://github.com/nextcloud/news.git cd news make If you are using a stable Nextcloud release, stay with the latest git tag release which is running on your version . To get an overview over all existing tags run: git tag You can switch to a release which will be supported on your installation by running: git checkout tags/TAG make # if News version >= 8.0.0 For instance, to use the 5.2.8 release, run: git checkout tags/5.2.8 Activate the News app in the apps menu To update the News app use change into the nextcloud/apps/news/ directory using your terminal and then run: git pull --rebase origin master make","title":"Build Dependencies"},{"location":"install/#uninstall-with-cleanup","text":"First uninstall the app via the web-interface or via occ: ./occ app:remove news This currently does not remove any of the database tables. Data in your /tmp directory will be automatically deleted by the OS. If you changed the temporary directory for Nextcloud you need to check on your own. Careful, this next part is only intended for admins, that know what they are doing. To remove the tables from the DB we drop the tables of news. Your installation might have a different prefix then oc_ but it is the default in most installations. Connect to your DB and execute the commands. Don't forget to switch to the right database. For example in mysql: use nextcloud; DROP TABLE oc_news_folders; DROP TABLE oc_news_feeds; DROP TABLE oc_news_items; Then we remove the traces in the migrations table. DELETE FROM oc_migrations WHERE app='news'; Next delete the app configuration. DELETE FROM oc_appconfig WHERE appid = 'news'; And finally remove the jobs from the job table. The last two lines are only needed for older installations. DELETE FROM oc_jobs WHERE class='OCA\\\\News\\\\Cron\\\\UpdaterJob'; DELETE FROM oc_jobs WHERE class='OCA\\\\News\\\\Cron\\\\Updater'; DELETE FROM oc_jobs WHERE argument='[\"OCA\\\\\\\\News\\\\\\\\Cron\\\\\\\\Updater\",\"run\"]'; Now nothing is left from news in your nextcloud installation.","title":"Uninstall with cleanup"},{"location":"maintenance/","text":"Maintenance Release Releases are created automatically by GitHub Actions. A release is triggered via a GitHub Release. The GitHub Action will then start a build based on the git tag. A release can only be approved by @Grotax or @SMillerDev . An admin of the Nextcloud organization can always overwrite these settings. The private key is stored as environmental secret in GitHub. The owner of the private key is @Grotax . Support PHP While the app should try to support all PHP versions that Nextcloud currently supports, the real focus when deciding to cut a PHP version should be on maintenance burden. Users are nice, but devs should be a priority in decisions that are likely to impact them significantly. Issues Bug reports without test cases (feed URL and action is enough) can be closed with or without comment. Feature requests without thoughtful commentary or pull request can be closed with or without comment, unless a developer is interested to support such a feature. Issues without activity in the last 30 days can be closed with or without comment. If this is a bug you care about that isn't getting attention, fix it. If you're good enough to understand the bug, you're good enough to fix it. Largely inspired by https://gist.github.com/ryanflorence/124070e7c4b3839d4573","title":"Maintenance"},{"location":"maintenance/#maintenance","text":"","title":"Maintenance"},{"location":"maintenance/#release","text":"Releases are created automatically by GitHub Actions. A release is triggered via a GitHub Release. The GitHub Action will then start a build based on the git tag. A release can only be approved by @Grotax or @SMillerDev . An admin of the Nextcloud organization can always overwrite these settings. The private key is stored as environmental secret in GitHub. The owner of the private key is @Grotax .","title":"Release"},{"location":"maintenance/#support","text":"","title":"Support"},{"location":"maintenance/#php","text":"While the app should try to support all PHP versions that Nextcloud currently supports, the real focus when deciding to cut a PHP version should be on maintenance burden. Users are nice, but devs should be a priority in decisions that are likely to impact them significantly.","title":"PHP"},{"location":"maintenance/#issues","text":"Bug reports without test cases (feed URL and action is enough) can be closed with or without comment. Feature requests without thoughtful commentary or pull request can be closed with or without comment, unless a developer is interested to support such a feature. Issues without activity in the last 30 days can be closed with or without comment. If this is a bug you care about that isn't getting attention, fix it. If you're good enough to understand the bug, you're good enough to fix it. Largely inspired by https://gist.github.com/ryanflorence/124070e7c4b3839d4573","title":"Issues"},{"location":"api/api-v1-2/","text":"External API v1-2 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 : 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 a 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: $.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: 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: { \"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. API Endpoints Folders Get all folders Status : Implemented Method : GET Route : /folders Parameters : none Returns : { \"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 : { \"name\": \"folder name\" } Return codes : HTTP 409 : If the folder exists already HTTP 422 : If the folder name is invalid (for instance empty) Returns : { \"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 : { \"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 : { // 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 : { \"feeds\": [ { \"id\": 39, \"url\": \"http://feeds.feedburner.com/oatmealfeed\", \"title\": \"The Oatmeal - Comics, Quizzes, & Stories\", \"faviconLink\": \"http://theoatmeal.com/favicon.ico\", \"added\": 1367063790, \"folderId\": 4, \"unreadCount\": 9, \"ordering\": 0, // 0 means no special ordering, 1 means oldest first, 2 newest first, new in 5.1.0 \"link\": \"http://theoatmeal.com/\", \"pinned\": true // if a feed should be sorted before other feeds, added in 6.0.3, \"updateErrorCount\": 0, // added in 8.6.0, 0 if no errors occured during the last update, // otherwise is incremented for each failed update. // Once it reaches a threshold, a message should be displayed to the user // indicating that the feed has failed to update that many times. // The webapp displays the message after 50 failed updates \"lastUpdateError\": \"error message here\" // added in 8.6.0, empty string or null if no update // error happened, otherwise contains the last update error message }, // etc ], \"starredCount\": 2, \"newestItemId\": 3443 // only sent if there are items } Create a feed Creates a new feed and returns the feed Status : Implemented Method : POST Route : /feeds Parameters : { \"url\": \"http:\\/\\/www.cyanogenmod.org\\/wp-content\\/themes\\/cyanogenmod\\/images\\/favicon.ico\", \"folderId\": 81 // id of the parent folder, null for root } Return codes : HTTP 409 : If the feed exists already HTTP 422 : If the feed cant be read (most likely contains errors) Returns : { \"feeds\": [ { \"id\": 39, \"url\": \"http://feeds.feedburner.com/oatmealfeed\", \"title\": \"The Oatmeal - Comics, Quizzes, & Stories\", \"faviconLink\": \"http://theoatmeal.com/favicon.ico\", \"added\": 1367063790, \"folderId\": 4, \"unreadCount\": 9, \"ordering\": 0, // 0 means no special ordering, 1 means oldest first, 2 newest first, new in 5.1.0 \"link\": \"http://theoatmeal.com/\", \"pinned\": true // if a feed should be sorted before other feeds, added in 6.0.3 } ], \"newestItemId\": 23 // only sent if there are items } Delete a feed Deletes a feed with the id feedId and all of its items Status : Implemented Method : DELETE Route : /feeds/{feedId} Parameters : none Return codes : HTTP 404 : If the feed does not exist Returns : nothing Move a feed to a different folder Status : Implemented Method : PUT Route : /feeds/{feedId}/move Parameters : { \"folderId\": null // id of the parent folder, null for root } Return codes : HTTP 404 : If the feed does not exist Returns : nothing Rename a feed Status : Implemented in 1.807 Method : PUT Route : /feeds/{feedId}/rename Parameters : { \"feedTitle\": \"New Title\" } Return codes : HTTP 404 : If the feed does not exist Returns : nothing Mark items of a feed as read Status : Implemented Method : PUT Route : /feeds/{feedId}/read Parameters : { // 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 Items Sanitation The following attributes are not sanitized meaning: including them in your web application can lead to XSS: title author url enclosureMime enclosureLink mediaThumbnail mediaDescription Types Name Default Types author null string|null body string|null contentHash string|null enclosureLink string|null enclosureMime string|null feedId int fingerprint string|null guid string guidHash string id int lastModified \\\"0\\\" string|null mediaDescription string|null mediaThumbnail string|null pubDate int|null rtl false bool starred false bool title string|null unread false bool updatedDate string|null url string|null Get items Status : Implemented Method : GET Route : /items Parameters : { \"batchSize\": 10, // the number of items that should be returned, defaults to -1, new in 5.2.3: -1 returns all items \"offset\": 30, // only return older (lower than equal that id) items than the one with id 30 \"type\": 1, // the type of the query (Feed: 0, Folder: 1, Starred: 2, All: 3) \"id\": 12, // the id of the folder or feed, Use 0 for Starred and All \"getRead\": true, // if true it returns all items, false returns only unread items \"oldestFirst\": false // implemented in 3.002, if true it reverse the sort order } Returns : { \"items\": [ { \"id\": 3443, \"guid\": \"http://grulja.wordpress.com/?p=76\", \"guidHash\": \"3059047a572cd9cd5d0bf645faffd077\", \"url\": \"http://grulja.wordpress.com/2013/04/29/plasma-nm-after-the-solid-sprint/\", \"title\": \"Plasma-nm after the solid sprint\", \"author\": \"Jan Grulich (grulja)\", \"pubDate\": 1367270544, \"body\": \"<p>At first I have to say...</p>\", \"enclosureMime\": null, \"enclosureLink\": null, \"mediaThumbnail\": null, // new in 14.1.4-rc1 \"mediaDescription\": null, // new in 14.1.4-rc1 \"feedId\": 67, \"unread\": true, \"starred\": false, \"rtl\": false, // new in 6.0.2 \"lastModified\": 1367273003, \"fingerprint\": \"aeaae2123\" // new in 8.4.0 hash over title, enclosures, body and url. Same fingerprint means same item and it's advised to locally mark the other one read as well and filter out duplicates in folder and all articles view }, // etc ] } Example Autopaging would work like this: Get the first 20 items from a feed with id 12 GET /items : { \"batchSize\": 20, \"offset\": 0, \"type\": 1, \"id\": 12, \"getRead\": false } The item with the lowest item id is 43. Get the next 20 items: GET /items : { \"batchSize\": 20, \"offset\": 43, \"type\": 1, \"id\": 12, \"getRead\": false } Get updated items This is used to stay up to date. Status : Implemented Method : GET Route : /items/updated Parameters : { \"lastModified\": 123231, // returns only items with a lastModified timestamp >= than this one // this may also return already existing items whose read or starred status // has been changed \"type\": 1, // the type of the query (Feed: 0, Folder: 1, Starred: 2, All: 3) \"id\": 12 // the id of the folder or feed, Use 0 for Starred and All } Returns : { \"items\": [ { \"id\": 3443, \"guid\": \"http://grulja.wordpress.com/?p=76\", \"guidHash\": \"3059047a572cd9cd5d0bf645faffd077\", \"url\": \"http://grulja.wordpress.com/2013/04/29/plasma-nm-after-the-solid-sprint/\", \"title\": \"Plasma-nm after the solid sprint\", \"author\": \"Jan Grulich (grulja)\", \"pubDate\": 1367270544, \"body\": \"<p>At first I have to say...</p>\", \"enclosureMime\": null, \"enclosureLink\": null, \"feedId\": 67, \"unread\": true, \"starred\": false, \"lastModified\": 1367273003, \"fingerprint\": \"aeaae2123\" // new in 8.4.0 hash over title, enclosures, body and url. Same fingerprint means same item and it's advised to locally mark the other one read as well and filter out duplicates in folder and all articles view }, // etc ] } Mark an item as read Status : Implemented Method : PUT Route : /items/{itemId}/read Parameters : none Return codes : HTTP 404 : If the item does not exist Returns : nothing Mark multiple items as read Status : Implemented in 1.2 Method : PUT Route : /items/read/multiple Parameters : { \"items\": [2, 3] // ids of the items } Returns : nothing Mark an item as unread Status : Implemented Method : PUT Route : /items/{itemId}/unread Parameters : none Return codes : HTTP 404 : If the item does not exist Returns : nothing Mark multiple items as unread Status : Implemented in 1.2 Method : PUT Route : /items/unread/multiple Parameters : { \"items\": [2, 3] // ids of the items } Returns : nothing Mark an item as starred Status : Implemented Method : PUT Route : /items/{feedId}/{guidHash}/star Parameters : none Return codes : HTTP 404 : If the item does not exist Returns : nothing Mark multiple items as starred Status : Implemented in 1.2 Method : PUT Route : /items/star/multiple Parameters : { \"items\": [ { \"feedId\": 3, \"guidHash\": \"sdf\" }, // etc ] } Returns : nothing Mark an item as unstarred Status : Implemented Method : PUT Route : /items/{feedId}/{guidHash}/unstar Parameters : none Return codes : HTTP 404 : If the item does not exist Returns : nothing Mark multiple items as unstarred Status : Implemented in 1.2 Method : PUT Route : /items/unstar/multiple Parameters : { \"items\": [ { \"feedId\": 3, \"guidHash\": \"sdf\" }, // etc ] } Returns : nothing Mark all items as read Status : Implemented Method : PUT Route : /items/read Parameters : { // 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 Updater To enable people to write their own update scripts instead of relying on the sequential built in web and system cron, API routes and console commands have been created. Updating should be done in the following fashion: Run the cleanup before the update Get all feeds and user ids For each feed and user id, run the update command Run the cleanup after the update. This implementation in Python should give you a good idea how to design and run it. Trigger cleanup before update This is used to clean up the database. It deletes folders and feeds that are marked for deletion Status : Implemented in 1.601 Authentication : Requires admin user Method : GET Route : /cleanup/before-update Returns : Nothing New in 8.1.0 : The console command for achieving the same result is: php -f nextcloud/occ news:updater:before-update Get feed ids and usernames for all feeds Status : Implemented in 1.203 Authentication : Requires admin user Method : GET Route : /feeds/all Parameters : none Returns : { \"feeds\": [ { \"id\": 39, \"userId\": \"john\", }, // etc ] } New in 8.1.0, Removed in 16.0.0 : The console command for achieving the same result is: php -f nextcloud/occ news:updater:all-feeds Trigger a feed update Status : Implemented in 1.601 Authentication : Requires admin user Method : GET Route : /feeds/update Parameters : { \"userId\": \"john\", \"feedId\": 3 } Return codes : HTTP 404 : If the feed does not exist Returns : Nothing New in 8.1.0 : The console command for achieving the same result is: php -f nextcloud/occ news:updater:update-feed 3 john Trigger cleanup after update This is used to clean up the database. It removes old read articles which are not starred Status : Implemented in 1.601 Authentication : Requires admin user Method : GET Route : /cleanup/after-update Returns : Nothing New in 8.1.0 : The console command for achieving the same result is: php -f nextcloud/occ news:updater:after-update Version Get the version Status : Implemented Method : GET Route : /version Parameters : none Returns : { \"version\": \"5.2.3\" } Status This API can be used to display warnings and errors in your client if the web app is improperly configured or not working. It is a good idea to call this route on like every 10th update and after the server connection parameters have been changed since it's likely that the user set up a new instance and configured the app improperly. Get the status Status : Implemented in 5.2.4 Method : GET Route : /status Parameters : none Returns : { \"version\": \"5.2.4\", \"warnings\": { \"improperlyConfiguredCron\": false, // if true the webapp will fail to update the feeds correctly \"incorrectDbCharset\": false } } If improperlyConfiguredCron is true you should display a warning that the app will not receive updates properly. This is due to the fact that the installation runs the cron in ajax mode to update the feeds. This is the default if you don't change anything and means that the app will only receive feed updates if the webinterface is accessed which will lead to lost updates. You should show the following warning and the link should be clickable: The News App updater is improperly configured and you will lose updates. See http://hisdomain.com/index.php/apps/news for instructions on how to fix it. If incorrectDbCharset is true you should display a warning that database charset is set up incorrectly and updates with unicode characters might fail User This API can be used to retrieve metadata about the current user. DEPRECATED: This API is deprecated, use the Nextcloud APIs instead. - https://docs.nextcloud.com/server/latest/developer_manual/client_apis/OCS/ocs-api-overview.html#user-metadata for user data - https://nc.url/avatar/{userid}/{size}?v={1|2} for the avatar Get the status Status : Implemented in 6.0.5 Method : GET Route : /user Parameters : none Returns : { \"userId\": \"john\", \"displayName\": \"John Doe\", \"lastLoginTimestamp\": 1241231233, // unix timestamp \"avatar\": { // if no avatar exists, this is null \"data\": \"asdiufadfasdfjlkjlkjljdfdf\", // base64 encoded image \"mime\": \"image/jpeg\" } }","title":"API v1.2"},{"location":"api/api-v1-2/#external-api-v1-2","text":"The News app 1.2 offers a RESTful API","title":"External API v1-2"},{"location":"api/api-v1-2/#api-stability-contract","text":"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","title":"API stability contract"},{"location":"api/api-v1-2/#authentication-basics","text":"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 : Authorization: Basic $CREDENTIALS where $CREDENTIALS is: base64(USER:PASSWORD)","title":"Authentication &amp; Basics"},{"location":"api/api-v1-2/#how-to-sync","text":"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)","title":"How To Sync"},{"location":"api/api-v1-2/#initial-sync","text":"The intial sync happens, when a user adds a 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.","title":"Initial Sync"},{"location":"api/api-v1-2/#syncing","text":"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","title":"Syncing"},{"location":"api/api-v1-2/#accessing-api-from-a-web-application","text":"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: $.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: 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 }); }]);","title":"Accessing API from a web application"},{"location":"api/api-v1-2/#input","text":"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: { \"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","title":"Input"},{"location":"api/api-v1-2/#output","text":"The output is JSON.","title":"Output"},{"location":"api/api-v1-2/#api-endpoints","text":"","title":"API Endpoints"},{"location":"api/api-v1-2/#folders","text":"","title":"Folders"},{"location":"api/api-v1-2/#get-all-folders","text":"Status : Implemented Method : GET Route : /folders Parameters : none Returns : { \"folders\": [ { \"id\": 4, \"name\": \"Media\" }, // etc ] }","title":"Get all folders"},{"location":"api/api-v1-2/#create-a-folder","text":"Creates a new folder and returns a new folder object Status : Implemented Method : POST Route : /folders Parameters : { \"name\": \"folder name\" } Return codes : HTTP 409 : If the folder exists already HTTP 422 : If the folder name is invalid (for instance empty) Returns : { \"folders\": [ { \"id\": 4, \"name\": \"Media\" } ] }","title":"Create a folder"},{"location":"api/api-v1-2/#delete-a-folder","text":"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","title":"Delete a folder"},{"location":"api/api-v1-2/#rename-a-folder","text":"Only the name can be updated Status : Implemented Method : PUT Route : /folders/{folderId} Parameters : { \"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","title":"Rename a folder"},{"location":"api/api-v1-2/#mark-items-of-a-folder-as-read","text":"Status : Implemented Method : PUT Route : /folders/{folderId}/read Parameters : { // 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","title":"Mark items of a folder as read"},{"location":"api/api-v1-2/#feeds","text":"","title":"Feeds"},{"location":"api/api-v1-2/#sanitation","text":"The following attributes are not sanitized meaning: including them in your web application can lead to XSS: title link","title":"Sanitation"},{"location":"api/api-v1-2/#get-all-feeds","text":"Status : Implemented Method : GET Route : /feeds Parameters : none Returns : { \"feeds\": [ { \"id\": 39, \"url\": \"http://feeds.feedburner.com/oatmealfeed\", \"title\": \"The Oatmeal - Comics, Quizzes, & Stories\", \"faviconLink\": \"http://theoatmeal.com/favicon.ico\", \"added\": 1367063790, \"folderId\": 4, \"unreadCount\": 9, \"ordering\": 0, // 0 means no special ordering, 1 means oldest first, 2 newest first, new in 5.1.0 \"link\": \"http://theoatmeal.com/\", \"pinned\": true // if a feed should be sorted before other feeds, added in 6.0.3, \"updateErrorCount\": 0, // added in 8.6.0, 0 if no errors occured during the last update, // otherwise is incremented for each failed update. // Once it reaches a threshold, a message should be displayed to the user // indicating that the feed has failed to update that many times. // The webapp displays the message after 50 failed updates \"lastUpdateError\": \"error message here\" // added in 8.6.0, empty string or null if no update // error happened, otherwise contains the last update error message }, // etc ], \"starredCount\": 2, \"newestItemId\": 3443 // only sent if there are items }","title":"Get all feeds"},{"location":"api/api-v1-2/#create-a-feed","text":"Creates a new feed and returns the feed Status : Implemented Method : POST Route : /feeds Parameters : { \"url\": \"http:\\/\\/www.cyanogenmod.org\\/wp-content\\/themes\\/cyanogenmod\\/images\\/favicon.ico\", \"folderId\": 81 // id of the parent folder, null for root } Return codes : HTTP 409 : If the feed exists already HTTP 422 : If the feed cant be read (most likely contains errors) Returns : { \"feeds\": [ { \"id\": 39, \"url\": \"http://feeds.feedburner.com/oatmealfeed\", \"title\": \"The Oatmeal - Comics, Quizzes, & Stories\", \"faviconLink\": \"http://theoatmeal.com/favicon.ico\", \"added\": 1367063790, \"folderId\": 4, \"unreadCount\": 9, \"ordering\": 0, // 0 means no special ordering, 1 means oldest first, 2 newest first, new in 5.1.0 \"link\": \"http://theoatmeal.com/\", \"pinned\": true // if a feed should be sorted before other feeds, added in 6.0.3 } ], \"newestItemId\": 23 // only sent if there are items }","title":"Create a feed"},{"location":"api/api-v1-2/#delete-a-feed","text":"Deletes a feed with the id feedId and all of its items Status : Implemented Method : DELETE Route : /feeds/{feedId} Parameters : none Return codes : HTTP 404 : If the feed does not exist Returns : nothing","title":"Delete a feed"},{"location":"api/api-v1-2/#move-a-feed-to-a-different-folder","text":"Status : Implemented Method : PUT Route : /feeds/{feedId}/move Parameters : { \"folderId\": null // id of the parent folder, null for root } Return codes : HTTP 404 : If the feed does not exist Returns : nothing","title":"Move a feed to a different folder"},{"location":"api/api-v1-2/#rename-a-feed","text":"Status : Implemented in 1.807 Method : PUT Route : /feeds/{feedId}/rename Parameters : { \"feedTitle\": \"New Title\" } Return codes : HTTP 404 : If the feed does not exist Returns : nothing","title":"Rename a feed"},{"location":"api/api-v1-2/#mark-items-of-a-feed-as-read","text":"Status : Implemented Method : PUT Route : /feeds/{feedId}/read Parameters : { // 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","title":"Mark items of a feed as read"},{"location":"api/api-v1-2/#items","text":"","title":"Items"},{"location":"api/api-v1-2/#sanitation_1","text":"The following attributes are not sanitized meaning: including them in your web application can lead to XSS: title author url enclosureMime enclosureLink mediaThumbnail mediaDescription","title":"Sanitation"},{"location":"api/api-v1-2/#types","text":"Name Default Types author null string|null body string|null contentHash string|null enclosureLink string|null enclosureMime string|null feedId int fingerprint string|null guid string guidHash string id int lastModified \\\"0\\\" string|null mediaDescription string|null mediaThumbnail string|null pubDate int|null rtl false bool starred false bool title string|null unread false bool updatedDate string|null url string|null","title":"Types"},{"location":"api/api-v1-2/#get-items","text":"Status : Implemented Method : GET Route : /items Parameters : { \"batchSize\": 10, // the number of items that should be returned, defaults to -1, new in 5.2.3: -1 returns all items \"offset\": 30, // only return older (lower than equal that id) items than the one with id 30 \"type\": 1, // the type of the query (Feed: 0, Folder: 1, Starred: 2, All: 3) \"id\": 12, // the id of the folder or feed, Use 0 for Starred and All \"getRead\": true, // if true it returns all items, false returns only unread items \"oldestFirst\": false // implemented in 3.002, if true it reverse the sort order } Returns : { \"items\": [ { \"id\": 3443, \"guid\": \"http://grulja.wordpress.com/?p=76\", \"guidHash\": \"3059047a572cd9cd5d0bf645faffd077\", \"url\": \"http://grulja.wordpress.com/2013/04/29/plasma-nm-after-the-solid-sprint/\", \"title\": \"Plasma-nm after the solid sprint\", \"author\": \"Jan Grulich (grulja)\", \"pubDate\": 1367270544, \"body\": \"<p>At first I have to say...</p>\", \"enclosureMime\": null, \"enclosureLink\": null, \"mediaThumbnail\": null, // new in 14.1.4-rc1 \"mediaDescription\": null, // new in 14.1.4-rc1 \"feedId\": 67, \"unread\": true, \"starred\": false, \"rtl\": false, // new in 6.0.2 \"lastModified\": 1367273003, \"fingerprint\": \"aeaae2123\" // new in 8.4.0 hash over title, enclosures, body and url. Same fingerprint means same item and it's advised to locally mark the other one read as well and filter out duplicates in folder and all articles view }, // etc ] }","title":"Get items"},{"location":"api/api-v1-2/#example","text":"Autopaging would work like this: Get the first 20 items from a feed with id 12 GET /items : { \"batchSize\": 20, \"offset\": 0, \"type\": 1, \"id\": 12, \"getRead\": false } The item with the lowest item id is 43. Get the next 20 items: GET /items : { \"batchSize\": 20, \"offset\": 43, \"type\": 1, \"id\": 12, \"getRead\": false }","title":"Example"},{"location":"api/api-v1-2/#get-updated-items","text":"This is used to stay up to date. Status : Implemented Method : GET Route : /items/updated Parameters : { \"lastModified\": 123231, // returns only items with a lastModified timestamp >= than this one // this may also return already existing items whose read or starred status // has been changed \"type\": 1, // the type of the query (Feed: 0, Folder: 1, Starred: 2, All: 3) \"id\": 12 // the id of the folder or feed, Use 0 for Starred and All } Returns : { \"items\": [ { \"id\": 3443, \"guid\": \"http://grulja.wordpress.com/?p=76\", \"guidHash\": \"3059047a572cd9cd5d0bf645faffd077\", \"url\": \"http://grulja.wordpress.com/2013/04/29/plasma-nm-after-the-solid-sprint/\", \"title\": \"Plasma-nm after the solid sprint\", \"author\": \"Jan Grulich (grulja)\", \"pubDate\": 1367270544, \"body\": \"<p>At first I have to say...</p>\", \"enclosureMime\": null, \"enclosureLink\": null, \"feedId\": 67, \"unread\": true, \"starred\": false, \"lastModified\": 1367273003, \"fingerprint\": \"aeaae2123\" // new in 8.4.0 hash over title, enclosures, body and url. Same fingerprint means same item and it's advised to locally mark the other one read as well and filter out duplicates in folder and all articles view }, // etc ] }","title":"Get updated items"},{"location":"api/api-v1-2/#mark-an-item-as-read","text":"Status : Implemented Method : PUT Route : /items/{itemId}/read Parameters : none Return codes : HTTP 404 : If the item does not exist Returns : nothing","title":"Mark an item as read"},{"location":"api/api-v1-2/#mark-multiple-items-as-read","text":"Status : Implemented in 1.2 Method : PUT Route : /items/read/multiple Parameters : { \"items\": [2, 3] // ids of the items } Returns : nothing","title":"Mark multiple items as read"},{"location":"api/api-v1-2/#mark-an-item-as-unread","text":"Status : Implemented Method : PUT Route : /items/{itemId}/unread Parameters : none Return codes : HTTP 404 : If the item does not exist Returns : nothing","title":"Mark an item as unread"},{"location":"api/api-v1-2/#mark-multiple-items-as-unread","text":"Status : Implemented in 1.2 Method : PUT Route : /items/unread/multiple Parameters : { \"items\": [2, 3] // ids of the items } Returns : nothing","title":"Mark multiple items as unread"},{"location":"api/api-v1-2/#mark-an-item-as-starred","text":"Status : Implemented Method : PUT Route : /items/{feedId}/{guidHash}/star Parameters : none Return codes : HTTP 404 : If the item does not exist Returns : nothing","title":"Mark an item as starred"},{"location":"api/api-v1-2/#mark-multiple-items-as-starred","text":"Status : Implemented in 1.2 Method : PUT Route : /items/star/multiple Parameters : { \"items\": [ { \"feedId\": 3, \"guidHash\": \"sdf\" }, // etc ] } Returns : nothing","title":"Mark multiple items as starred"},{"location":"api/api-v1-2/#mark-an-item-as-unstarred","text":"Status : Implemented Method : PUT Route : /items/{feedId}/{guidHash}/unstar Parameters : none Return codes : HTTP 404 : If the item does not exist Returns : nothing","title":"Mark an item as unstarred"},{"location":"api/api-v1-2/#mark-multiple-items-as-unstarred","text":"Status : Implemented in 1.2 Method : PUT Route : /items/unstar/multiple Parameters : { \"items\": [ { \"feedId\": 3, \"guidHash\": \"sdf\" }, // etc ] } Returns : nothing","title":"Mark multiple items as unstarred"},{"location":"api/api-v1-2/#mark-all-items-as-read","text":"Status : Implemented Method : PUT Route : /items/read Parameters : { // 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","title":"Mark all items as read"},{"location":"api/api-v1-2/#updater","text":"To enable people to write their own update scripts instead of relying on the sequential built in web and system cron, API routes and console commands have been created. Updating should be done in the following fashion: Run the cleanup before the update Get all feeds and user ids For each feed and user id, run the update command Run the cleanup after the update. This implementation in Python should give you a good idea how to design and run it.","title":"Updater"},{"location":"api/api-v1-2/#trigger-cleanup-before-update","text":"This is used to clean up the database. It deletes folders and feeds that are marked for deletion Status : Implemented in 1.601 Authentication : Requires admin user Method : GET Route : /cleanup/before-update Returns : Nothing New in 8.1.0 : The console command for achieving the same result is: php -f nextcloud/occ news:updater:before-update","title":"Trigger cleanup before update"},{"location":"api/api-v1-2/#get-feed-ids-and-usernames-for-all-feeds","text":"Status : Implemented in 1.203 Authentication : Requires admin user Method : GET Route : /feeds/all Parameters : none Returns : { \"feeds\": [ { \"id\": 39, \"userId\": \"john\", }, // etc ] } New in 8.1.0, Removed in 16.0.0 : The console command for achieving the same result is: php -f nextcloud/occ news:updater:all-feeds","title":"Get feed ids and usernames for all feeds"},{"location":"api/api-v1-2/#trigger-a-feed-update","text":"Status : Implemented in 1.601 Authentication : Requires admin user Method : GET Route : /feeds/update Parameters : { \"userId\": \"john\", \"feedId\": 3 } Return codes : HTTP 404 : If the feed does not exist Returns : Nothing New in 8.1.0 : The console command for achieving the same result is: php -f nextcloud/occ news:updater:update-feed 3 john","title":"Trigger a feed update"},{"location":"api/api-v1-2/#trigger-cleanup-after-update","text":"This is used to clean up the database. It removes old read articles which are not starred Status : Implemented in 1.601 Authentication : Requires admin user Method : GET Route : /cleanup/after-update Returns : Nothing New in 8.1.0 : The console command for achieving the same result is: php -f nextcloud/occ news:updater:after-update","title":"Trigger cleanup after update"},{"location":"api/api-v1-2/#version","text":"","title":"Version"},{"location":"api/api-v1-2/#get-the-version","text":"Status : Implemented Method : GET Route : /version Parameters : none Returns : { \"version\": \"5.2.3\" }","title":"Get the version"},{"location":"api/api-v1-2/#status","text":"This API can be used to display warnings and errors in your client if the web app is improperly configured or not working. It is a good idea to call this route on like every 10th update and after the server connection parameters have been changed since it's likely that the user set up a new instance and configured the app improperly.","title":"Status"},{"location":"api/api-v1-2/#get-the-status","text":"Status : Implemented in 5.2.4 Method : GET Route : /status Parameters : none Returns : { \"version\": \"5.2.4\", \"warnings\": { \"improperlyConfiguredCron\": false, // if true the webapp will fail to update the feeds correctly \"incorrectDbCharset\": false } } If improperlyConfiguredCron is true you should display a warning that the app will not receive updates properly. This is due to the fact that the installation runs the cron in ajax mode to update the feeds. This is the default if you don't change anything and means that the app will only receive feed updates if the webinterface is accessed which will lead to lost updates. You should show the following warning and the link should be clickable: The News App updater is improperly configured and you will lose updates. See http://hisdomain.com/index.php/apps/news for instructions on how to fix it. If incorrectDbCharset is true you should display a warning that database charset is set up incorrectly and updates with unicode characters might fail","title":"Get the status"},{"location":"api/api-v1-2/#user","text":"This API can be used to retrieve metadata about the current user. DEPRECATED: This API is deprecated, use the Nextcloud APIs instead. - https://docs.nextcloud.com/server/latest/developer_manual/client_apis/OCS/ocs-api-overview.html#user-metadata for user data - https://nc.url/avatar/{userid}/{size}?v={1|2} for the avatar","title":"User"},{"location":"api/api-v1-2/#get-the-status_1","text":"Status : Implemented in 6.0.5 Method : GET Route : /user Parameters : none Returns : { \"userId\": \"john\", \"displayName\": \"John Doe\", \"lastLoginTimestamp\": 1241231233, // unix timestamp \"avatar\": { // if no avatar exists, this is null \"data\": \"asdiufadfasdfjlkjlkjljdfdf\", // base64 encoded image \"mime\": \"image/jpeg\" } }","title":"Get the status"},{"location":"api/api-v1-3/","text":"External API v1-3 The News app 1.3 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-3/ 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-3/feeds Credentials need to be passed as an HTTP header using HTTP basic auth : 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-3) Initial Sync The intial sync happens, when a user adds a 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 {\"itemIds\": [1, 3, 5]} Notify the News app of unstarred articles : PUT /items/unstarred/multiple {\"itemIds\": [1, 3, 5]} 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: $.ajax({ type: 'GET', url: 'https://yournextcloud.com/index.php/apps/news/api/v1-3/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: 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-3/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: { \"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. API Endpoints Folders Get all folders Status : Implemented Method : GET Route : /folders Parameters : none Returns : { \"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 : { \"name\": \"folder name\" } Return codes : HTTP 409 : If the folder exists already HTTP 422 : If the folder name is invalid (for instance empty) Returns : { \"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 : { \"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 : POST Route : /folders/{folderId}/read Parameters : { // 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 : { \"feeds\": [ { \"id\": 39, \"url\": \"http://feeds.feedburner.com/oatmealfeed\", \"title\": \"The Oatmeal - Comics, Quizzes, & Stories\", \"faviconLink\": \"http://theoatmeal.com/favicon.ico\", \"added\": 1367063790, \"folderId\": 4, \"unreadCount\": 9, \"ordering\": 0, // 0 means no special ordering, 1 means oldest first, 2 newest first, new in 5.1.0 \"link\": \"http://theoatmeal.com/\", \"pinned\": true // if a feed should be sorted before other feeds, added in 6.0.3, \"updateErrorCount\": 0, // added in 8.6.0, 0 if no errors occured during the last update, // otherwise is incremented for each failed update. // Once it reaches a threshold, a message should be displayed to the user // indicating that the feed has failed to update that many times. // The webapp displays the message after 50 failed updates \"lastUpdateError\": \"error message here\" // added in 8.6.0, empty string or null if no update // error happened, otherwise contains the last update error message }, // etc ], \"starredCount\": 2, \"newestItemId\": 3443 // only sent if there are items } Create a feed Creates a new feed and returns the feed Status : Implemented Method : POST Route : /feeds Parameters : { \"url\": \"http:\\/\\/www.cyanogenmod.org\\/wp-content\\/themes\\/cyanogenmod\\/images\\/favicon.ico\", \"folderId\": 81 // id of the parent folder, null for root } Return codes : HTTP 409 : If the feed exists already HTTP 422 : If the feed cant be read (most likely contains errors) Returns : { \"feeds\": [ { \"id\": 39, \"url\": \"http://feeds.feedburner.com/oatmealfeed\", \"title\": \"The Oatmeal - Comics, Quizzes, & Stories\", \"faviconLink\": \"http://theoatmeal.com/favicon.ico\", \"added\": 1367063790, \"folderId\": 4, \"unreadCount\": 9, \"ordering\": 0, // 0 means no special ordering, 1 means oldest first, 2 newest first, new in 5.1.0 \"link\": \"http://theoatmeal.com/\", \"pinned\": true // if a feed should be sorted before other feeds, added in 6.0.3 } ], \"newestItemId\": 23 // only sent if there are items } Delete a feed Deletes a feed with the id feedId and all of its items Status : Implemented Method : DELETE Route : /feeds/{feedId} Parameters : none Return codes : HTTP 404 : If the feed does not exist Returns : nothing Move a feed to a different folder Status : Implemented Method : POST Route : /feeds/{feedId}/move Parameters : { \"folderId\": null // id of the parent folder, null for root } Return codes : HTTP 404 : If the feed does not exist Returns : nothing Rename a feed Status : Implemented in News 1.807 Method : POST Route : /feeds/{feedId}/rename Parameters : { \"feedTitle\": \"New Title\" } Return codes : HTTP 404 : If the feed does not exist Returns : nothing Mark items of a feed as read Status : Implemented Method : POST Route : /feeds/{feedId}/read Parameters : { // 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 Items Sanitation The following attributes are not sanitized meaning: including them in your web application can lead to XSS: title author url enclosureMime enclosureLink mediaThumbnail mediaDescription Types Name Default Types author null string|null body string|null contentHash string|null enclosureLink string|null enclosureMime string|null feedId int fingerprint string|null guid string guidHash string id int lastModified \\\"0\\\" string|null mediaDescription string|null mediaThumbnail string|null pubDate int|null rtl false bool starred false bool title string|null unread false bool updatedDate string|null url string|null Get items Status : Implemented Method : GET Route : /items Parameters : { \"batchSize\": 10, // the number of items that should be returned, defaults to -1, new in 5.2.3: -1 returns all items \"offset\": 30, // only return older (lower than equal that id) items than the one with id 30 \"type\": 1, // the type of the query (Feed: 0, Folder: 1, Starred: 2, All: 3) \"id\": 12, // the id of the folder or feed, Use 0 for Starred and All \"getRead\": true, // if true it returns all items, false returns only unread items \"oldestFirst\": false // implemented in 3.002, if true it reverse the sort order } Returns : { \"items\": [ { \"id\": 3443, \"guid\": \"http://grulja.wordpress.com/?p=76\", \"guidHash\": \"3059047a572cd9cd5d0bf645faffd077\", \"url\": \"http://grulja.wordpress.com/2013/04/29/plasma-nm-after-the-solid-sprint/\", \"title\": \"Plasma-nm after the solid sprint\", \"author\": \"Jan Grulich (grulja)\", \"pubDate\": 1367270544, \"body\": \"<p>At first I have to say...</p>\", \"enclosureMime\": null, \"enclosureLink\": null, \"mediaThumbnail\": null, // new in 14.1.4-rc1 \"mediaDescription\": null, // new in 14.1.4-rc1 \"feedId\": 67, \"unread\": true, \"starred\": false, \"rtl\": false, // new in 6.0.2 \"lastModified\": 1367273003, \"fingerprint\": \"aeaae2123\" // new in 8.4.0 hash over title, enclosures, body and url. Same fingerprint means same item and it's advised to locally mark the other one read as well and filter out duplicates in folder and all articles view }, // etc ] } Example Autopaging would work like this: Get the first 20 items from a feed with id 12 GET /items : { \"batchSize\": 20, \"offset\": 0, \"type\": 1, \"id\": 12, \"getRead\": false } The item with the lowest item id is 43. Get the next 20 items: GET /items : { \"batchSize\": 20, \"offset\": 43, \"type\": 1, \"id\": 12, \"getRead\": false } Get updated items This is used to stay up to date. Status : Implemented Method : GET Route : /items/updated Parameters : { \"lastModified\": 123231, // returns only items with a lastModified timestamp >= than this one // this may also return already existing items whose read or starred status // has been changed \"type\": 1, // the type of the query (Feed: 0, Folder: 1, Starred: 2, All: 3) \"id\": 12 // the id of the folder or feed, Use 0 for Starred and All } Returns : { \"items\": [ { \"id\": 3443, \"guid\": \"http://grulja.wordpress.com/?p=76\", \"guidHash\": \"3059047a572cd9cd5d0bf645faffd077\", \"url\": \"http://grulja.wordpress.com/2013/04/29/plasma-nm-after-the-solid-sprint/\", \"title\": \"Plasma-nm after the solid sprint\", \"author\": \"Jan Grulich (grulja)\", \"pubDate\": 1367270544, \"body\": \"<p>At first I have to say...</p>\", \"enclosureMime\": null, \"enclosureLink\": null, \"feedId\": 67, \"unread\": true, \"starred\": false, \"lastModified\": 1367273003, \"fingerprint\": \"aeaae2123\" // new in 8.4.0 hash over title, enclosures, body and url. Same fingerprint means same item and it's advised to locally mark the other one read as well and filter out duplicates in folder and all articles view }, // etc ] } Mark an item as read Status : Implemented Method : POST Route : /items/{itemId}/read Parameters : none Return codes : HTTP 404 : If the item does not exist Returns : nothing Mark multiple items as read Status : Implemented in API 1.2 Method : POST Route : /items/read/multiple Parameters : { \"itemIds\": [2, 3] // ids of the items } Returns : nothing Mark an item as unread Status : Implemented Method : POST Route : /items/{itemId}/unread Parameters : none Return codes : HTTP 404 : If the item does not exist Returns : nothing Mark multiple items as unread Status : Implemented in API 1.2 Method : POST Route : /items/unread/multiple Parameters : { \"itemIds\": [2, 3] // ids of the items } Returns : nothing Mark an item as starred Status : Implemented in API 1.3 Method : POST Route : /items/{itemId}/star Parameters : none Return codes : HTTP 404 : If the item does not exist Returns : nothing Mark multiple items as starred Status : Implemented in API 1.3 Method : POST Route : /items/star/multiple Parameters : { \"itemIds\": [1, ...] } Returns : nothing Mark an item as unstarred Status : Implemented in API 1.3 Method : POST Route : /items/{itemId}/unstar Parameters : none Return codes : HTTP 404 : If the item does not exist Returns : nothing Mark multiple items as unstarred Status : Implemented in API 1.3 Method : POST Route : /items/unstar/multiple Parameters : { \"itemIds\": [1, ...] } Returns : nothing Mark all items as read Status : Implemented Method : POST Route : /items/read Parameters : { // 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 Updater To enable people to write their own update scripts instead of relying on the sequential built in web and system cron, API routes and console commands have been created. Updating should be done in the following fashion: Run the cleanup before the update Get all feeds and user ids For each feed and user id, run the update command Run the cleanup after the update. This implementation in Python should give you a good idea how to design and run it. Trigger cleanup before update This is used to clean up the database. It deletes folders and feeds that are marked for deletion Status : Implemented in News 1.601 Authentication : Requires admin user Method : GET Route : /cleanup/before-update Returns : Nothing New in 8.1.0 : The console command for achieving the same result is: php -f nextcloud/occ news:updater:before-update Get feed ids and usernames for all feeds Status : Implemented in News 1.203 Authentication : Requires admin user Method : GET Route : /feeds/all Parameters : none Returns : { \"feeds\": [ { \"id\": 39, \"userId\": \"john\", }, // etc ] } New in 8.1.0, Removed in 16.0.0 : The console command for achieving the same result is: php -f nextcloud/occ news:updater:all-feeds Trigger a feed update Status : Implemented in News 1.601 Authentication : Requires admin user Method : GET Route : /feeds/update Parameters : { \"userId\": \"john\", \"feedId\": 3 } Return codes : HTTP 404 : If the feed does not exist Returns : Nothing New in 8.1.0 : The console command for achieving the same result is: php -f nextcloud/occ news:updater:update-feed 3 john Trigger cleanup after update This is used to clean up the database. It removes old read articles which are not starred Status : Implemented in News 1.601 Authentication : Requires admin user Method : GET Route : /cleanup/after-update Returns : Nothing New in 8.1.0 : The console command for achieving the same result is: php -f nextcloud/occ news:updater:after-update Version Get the version Status : Implemented Method : GET Route : /version Parameters : none Returns : { \"version\": \"5.2.3\" } Status This API can be used to display warnings and errors in your client if the web app is improperly configured or not working. It is a good idea to call this route on like every 10th update and after the server connection parameters have been changed since it's likely that the user set up a new instance and configured the app improperly. Get the status Status : Implemented in News 5.2.4 Method : GET Route : /status Parameters : none Returns : { \"version\": \"5.2.4\", \"warnings\": { \"improperlyConfiguredCron\": false, // if true the webapp will fail to update the feeds correctly \"incorrectDbCharset\": false } } If improperlyConfiguredCron is true you should display a warning that the app will not receive updates properly. This is due to the fact that the installation runs the cron in ajax mode to update the feeds. This is the default if you don't change anything and means that the app will only receive feed updates if the webinterface is accessed which will lead to lost updates. You should show the following warning and the link should be clickable: The News App updater is improperly configured and you will lose updates. See http://hisdomain.com/index.php/apps/news for instructions on how to fix it. If incorrectDbCharset is true you should display a warning that database charset is set up incorrectly and updates with unicode characters might fail User This API can be used to retrieve metadata about the current user. DEPRECATED: This API is deprecated, use the Nextcloud APIs instead. - https://docs.nextcloud.com/server/latest/developer_manual/client_apis/OCS/ocs-api-overview.html#user-metadata for user data - https://nc.url/avatar/{userid}/{size}?v={1|2} for the avatar Get the status Status : Implemented in News 6.0.5 Method : GET Route : /user Parameters : none Returns : { \"userId\": \"john\", \"displayName\": \"John Doe\", \"lastLoginTimestamp\": 1241231233, // unix timestamp \"avatar\": { // if no avatar exists, this is null \"data\": \"asdiufadfasdfjlkjlkjljdfdf\", // base64 encoded image \"mime\": \"image/jpeg\" } }","title":"API v1.3"},{"location":"api/api-v1-3/#external-api-v1-3","text":"The News app 1.3 offers a RESTful API.","title":"External API v1-3"},{"location":"api/api-v1-3/#api-stability-contract","text":"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","title":"API stability contract"},{"location":"api/api-v1-3/#authentication-basics","text":"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-3/ 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-3/feeds Credentials need to be passed as an HTTP header using HTTP basic auth : Authorization: Basic $CREDENTIALS where $CREDENTIALS is: base64(USER:PASSWORD)","title":"Authentication &amp; Basics"},{"location":"api/api-v1-3/#how-to-sync","text":"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-3)","title":"How To Sync"},{"location":"api/api-v1-3/#initial-sync","text":"The intial sync happens, when a user adds a 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.","title":"Initial Sync"},{"location":"api/api-v1-3/#syncing","text":"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 {\"itemIds\": [1, 3, 5]} Notify the News app of unstarred articles : PUT /items/unstarred/multiple {\"itemIds\": [1, 3, 5]} Get new folders : GET /folders Get new feeds : GET /feeds Get new items and modified items : GET /items/updated?lastModified=12123123123&type=3","title":"Syncing"},{"location":"api/api-v1-3/#accessing-api-from-a-web-application","text":"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: $.ajax({ type: 'GET', url: 'https://yournextcloud.com/index.php/apps/news/api/v1-3/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: 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-3/version' }).success(function (data, status, header, config) { // handle success }).error(function (data, status, header, config) { // handle error }); }]);","title":"Accessing API from a web application"},{"location":"api/api-v1-3/#input","text":"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: { \"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","title":"Input"},{"location":"api/api-v1-3/#output","text":"The output is JSON.","title":"Output"},{"location":"api/api-v1-3/#api-endpoints","text":"","title":"API Endpoints"},{"location":"api/api-v1-3/#folders","text":"","title":"Folders"},{"location":"api/api-v1-3/#get-all-folders","text":"Status : Implemented Method : GET Route : /folders Parameters : none Returns : { \"folders\": [ { \"id\": 4, \"name\": \"Media\" }, // etc ] }","title":"Get all folders"},{"location":"api/api-v1-3/#create-a-folder","text":"Creates a new folder and returns a new folder object Status : Implemented Method : POST Route : /folders Parameters : { \"name\": \"folder name\" } Return codes : HTTP 409 : If the folder exists already HTTP 422 : If the folder name is invalid (for instance empty) Returns : { \"folders\": [ { \"id\": 4, \"name\": \"Media\" } ] }","title":"Create a folder"},{"location":"api/api-v1-3/#delete-a-folder","text":"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","title":"Delete a folder"},{"location":"api/api-v1-3/#rename-a-folder","text":"Only the name can be updated Status : Implemented Method : PUT Route : /folders/{folderId} Parameters : { \"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","title":"Rename a folder"},{"location":"api/api-v1-3/#mark-items-of-a-folder-as-read","text":"Status : Implemented Method : POST Route : /folders/{folderId}/read Parameters : { // 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","title":"Mark items of a folder as read"},{"location":"api/api-v1-3/#feeds","text":"","title":"Feeds"},{"location":"api/api-v1-3/#sanitation","text":"The following attributes are not sanitized meaning: including them in your web application can lead to XSS: title link","title":"Sanitation"},{"location":"api/api-v1-3/#get-all-feeds","text":"Status : Implemented Method : GET Route : /feeds Parameters : none Returns : { \"feeds\": [ { \"id\": 39, \"url\": \"http://feeds.feedburner.com/oatmealfeed\", \"title\": \"The Oatmeal - Comics, Quizzes, & Stories\", \"faviconLink\": \"http://theoatmeal.com/favicon.ico\", \"added\": 1367063790, \"folderId\": 4, \"unreadCount\": 9, \"ordering\": 0, // 0 means no special ordering, 1 means oldest first, 2 newest first, new in 5.1.0 \"link\": \"http://theoatmeal.com/\", \"pinned\": true // if a feed should be sorted before other feeds, added in 6.0.3, \"updateErrorCount\": 0, // added in 8.6.0, 0 if no errors occured during the last update, // otherwise is incremented for each failed update. // Once it reaches a threshold, a message should be displayed to the user // indicating that the feed has failed to update that many times. // The webapp displays the message after 50 failed updates \"lastUpdateError\": \"error message here\" // added in 8.6.0, empty string or null if no update // error happened, otherwise contains the last update error message }, // etc ], \"starredCount\": 2, \"newestItemId\": 3443 // only sent if there are items }","title":"Get all feeds"},{"location":"api/api-v1-3/#create-a-feed","text":"Creates a new feed and returns the feed Status : Implemented Method : POST Route : /feeds Parameters : { \"url\": \"http:\\/\\/www.cyanogenmod.org\\/wp-content\\/themes\\/cyanogenmod\\/images\\/favicon.ico\", \"folderId\": 81 // id of the parent folder, null for root } Return codes : HTTP 409 : If the feed exists already HTTP 422 : If the feed cant be read (most likely contains errors) Returns : { \"feeds\": [ { \"id\": 39, \"url\": \"http://feeds.feedburner.com/oatmealfeed\", \"title\": \"The Oatmeal - Comics, Quizzes, & Stories\", \"faviconLink\": \"http://theoatmeal.com/favicon.ico\", \"added\": 1367063790, \"folderId\": 4, \"unreadCount\": 9, \"ordering\": 0, // 0 means no special ordering, 1 means oldest first, 2 newest first, new in 5.1.0 \"link\": \"http://theoatmeal.com/\", \"pinned\": true // if a feed should be sorted before other feeds, added in 6.0.3 } ], \"newestItemId\": 23 // only sent if there are items }","title":"Create a feed"},{"location":"api/api-v1-3/#delete-a-feed","text":"Deletes a feed with the id feedId and all of its items Status : Implemented Method : DELETE Route : /feeds/{feedId} Parameters : none Return codes : HTTP 404 : If the feed does not exist Returns : nothing","title":"Delete a feed"},{"location":"api/api-v1-3/#move-a-feed-to-a-different-folder","text":"Status : Implemented Method : POST Route : /feeds/{feedId}/move Parameters : { \"folderId\": null // id of the parent folder, null for root } Return codes : HTTP 404 : If the feed does not exist Returns : nothing","title":"Move a feed to a different folder"},{"location":"api/api-v1-3/#rename-a-feed","text":"Status : Implemented in News 1.807 Method : POST Route : /feeds/{feedId}/rename Parameters : { \"feedTitle\": \"New Title\" } Return codes : HTTP 404 : If the feed does not exist Returns : nothing","title":"Rename a feed"},{"location":"api/api-v1-3/#mark-items-of-a-feed-as-read","text":"Status : Implemented Method : POST Route : /feeds/{feedId}/read Parameters : { // 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","title":"Mark items of a feed as read"},{"location":"api/api-v1-3/#items","text":"","title":"Items"},{"location":"api/api-v1-3/#sanitation_1","text":"The following attributes are not sanitized meaning: including them in your web application can lead to XSS: title author url enclosureMime enclosureLink mediaThumbnail mediaDescription","title":"Sanitation"},{"location":"api/api-v1-3/#types","text":"Name Default Types author null string|null body string|null contentHash string|null enclosureLink string|null enclosureMime string|null feedId int fingerprint string|null guid string guidHash string id int lastModified \\\"0\\\" string|null mediaDescription string|null mediaThumbnail string|null pubDate int|null rtl false bool starred false bool title string|null unread false bool updatedDate string|null url string|null","title":"Types"},{"location":"api/api-v1-3/#get-items","text":"Status : Implemented Method : GET Route : /items Parameters : { \"batchSize\": 10, // the number of items that should be returned, defaults to -1, new in 5.2.3: -1 returns all items \"offset\": 30, // only return older (lower than equal that id) items than the one with id 30 \"type\": 1, // the type of the query (Feed: 0, Folder: 1, Starred: 2, All: 3) \"id\": 12, // the id of the folder or feed, Use 0 for Starred and All \"getRead\": true, // if true it returns all items, false returns only unread items \"oldestFirst\": false // implemented in 3.002, if true it reverse the sort order } Returns : { \"items\": [ { \"id\": 3443, \"guid\": \"http://grulja.wordpress.com/?p=76\", \"guidHash\": \"3059047a572cd9cd5d0bf645faffd077\", \"url\": \"http://grulja.wordpress.com/2013/04/29/plasma-nm-after-the-solid-sprint/\", \"title\": \"Plasma-nm after the solid sprint\", \"author\": \"Jan Grulich (grulja)\", \"pubDate\": 1367270544, \"body\": \"<p>At first I have to say...</p>\", \"enclosureMime\": null, \"enclosureLink\": null, \"mediaThumbnail\": null, // new in 14.1.4-rc1 \"mediaDescription\": null, // new in 14.1.4-rc1 \"feedId\": 67, \"unread\": true, \"starred\": false, \"rtl\": false, // new in 6.0.2 \"lastModified\": 1367273003, \"fingerprint\": \"aeaae2123\" // new in 8.4.0 hash over title, enclosures, body and url. Same fingerprint means same item and it's advised to locally mark the other one read as well and filter out duplicates in folder and all articles view }, // etc ] }","title":"Get items"},{"location":"api/api-v1-3/#example","text":"Autopaging would work like this: Get the first 20 items from a feed with id 12 GET /items : { \"batchSize\": 20, \"offset\": 0, \"type\": 1, \"id\": 12, \"getRead\": false } The item with the lowest item id is 43. Get the next 20 items: GET /items : { \"batchSize\": 20, \"offset\": 43, \"type\": 1, \"id\": 12, \"getRead\": false }","title":"Example"},{"location":"api/api-v1-3/#get-updated-items","text":"This is used to stay up to date. Status : Implemented Method : GET Route : /items/updated Parameters : { \"lastModified\": 123231, // returns only items with a lastModified timestamp >= than this one // this may also return already existing items whose read or starred status // has been changed \"type\": 1, // the type of the query (Feed: 0, Folder: 1, Starred: 2, All: 3) \"id\": 12 // the id of the folder or feed, Use 0 for Starred and All } Returns : { \"items\": [ { \"id\": 3443, \"guid\": \"http://grulja.wordpress.com/?p=76\", \"guidHash\": \"3059047a572cd9cd5d0bf645faffd077\", \"url\": \"http://grulja.wordpress.com/2013/04/29/plasma-nm-after-the-solid-sprint/\", \"title\": \"Plasma-nm after the solid sprint\", \"author\": \"Jan Grulich (grulja)\", \"pubDate\": 1367270544, \"body\": \"<p>At first I have to say...</p>\", \"enclosureMime\": null, \"enclosureLink\": null, \"feedId\": 67, \"unread\": true, \"starred\": false, \"lastModified\": 1367273003, \"fingerprint\": \"aeaae2123\" // new in 8.4.0 hash over title, enclosures, body and url. Same fingerprint means same item and it's advised to locally mark the other one read as well and filter out duplicates in folder and all articles view }, // etc ] }","title":"Get updated items"},{"location":"api/api-v1-3/#mark-an-item-as-read","text":"Status : Implemented Method : POST Route : /items/{itemId}/read Parameters : none Return codes : HTTP 404 : If the item does not exist Returns : nothing","title":"Mark an item as read"},{"location":"api/api-v1-3/#mark-multiple-items-as-read","text":"Status : Implemented in API 1.2 Method : POST Route : /items/read/multiple Parameters : { \"itemIds\": [2, 3] // ids of the items } Returns : nothing","title":"Mark multiple items as read"},{"location":"api/api-v1-3/#mark-an-item-as-unread","text":"Status : Implemented Method : POST Route : /items/{itemId}/unread Parameters : none Return codes : HTTP 404 : If the item does not exist Returns : nothing","title":"Mark an item as unread"},{"location":"api/api-v1-3/#mark-multiple-items-as-unread","text":"Status : Implemented in API 1.2 Method : POST Route : /items/unread/multiple Parameters : { \"itemIds\": [2, 3] // ids of the items } Returns : nothing","title":"Mark multiple items as unread"},{"location":"api/api-v1-3/#mark-an-item-as-starred","text":"Status : Implemented in API 1.3 Method : POST Route : /items/{itemId}/star Parameters : none Return codes : HTTP 404 : If the item does not exist Returns : nothing","title":"Mark an item as starred"},{"location":"api/api-v1-3/#mark-multiple-items-as-starred","text":"Status : Implemented in API 1.3 Method : POST Route : /items/star/multiple Parameters : { \"itemIds\": [1, ...] } Returns : nothing","title":"Mark multiple items as starred"},{"location":"api/api-v1-3/#mark-an-item-as-unstarred","text":"Status : Implemented in API 1.3 Method : POST Route : /items/{itemId}/unstar Parameters : none Return codes : HTTP 404 : If the item does not exist Returns : nothing","title":"Mark an item as unstarred"},{"location":"api/api-v1-3/#mark-multiple-items-as-unstarred","text":"Status : Implemented in API 1.3 Method : POST Route : /items/unstar/multiple Parameters : { \"itemIds\": [1, ...] } Returns : nothing","title":"Mark multiple items as unstarred"},{"location":"api/api-v1-3/#mark-all-items-as-read","text":"Status : Implemented Method : POST Route : /items/read Parameters : { // 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","title":"Mark all items as read"},{"location":"api/api-v1-3/#updater","text":"To enable people to write their own update scripts instead of relying on the sequential built in web and system cron, API routes and console commands have been created. Updating should be done in the following fashion: Run the cleanup before the update Get all feeds and user ids For each feed and user id, run the update command Run the cleanup after the update. This implementation in Python should give you a good idea how to design and run it.","title":"Updater"},{"location":"api/api-v1-3/#trigger-cleanup-before-update","text":"This is used to clean up the database. It deletes folders and feeds that are marked for deletion Status : Implemented in News 1.601 Authentication : Requires admin user Method : GET Route : /cleanup/before-update Returns : Nothing New in 8.1.0 : The console command for achieving the same result is: php -f nextcloud/occ news:updater:before-update","title":"Trigger cleanup before update"},{"location":"api/api-v1-3/#get-feed-ids-and-usernames-for-all-feeds","text":"Status : Implemented in News 1.203 Authentication : Requires admin user Method : GET Route : /feeds/all Parameters : none Returns : { \"feeds\": [ { \"id\": 39, \"userId\": \"john\", }, // etc ] } New in 8.1.0, Removed in 16.0.0 : The console command for achieving the same result is: php -f nextcloud/occ news:updater:all-feeds","title":"Get feed ids and usernames for all feeds"},{"location":"api/api-v1-3/#trigger-a-feed-update","text":"Status : Implemented in News 1.601 Authentication : Requires admin user Method : GET Route : /feeds/update Parameters : { \"userId\": \"john\", \"feedId\": 3 } Return codes : HTTP 404 : If the feed does not exist Returns : Nothing New in 8.1.0 : The console command for achieving the same result is: php -f nextcloud/occ news:updater:update-feed 3 john","title":"Trigger a feed update"},{"location":"api/api-v1-3/#trigger-cleanup-after-update","text":"This is used to clean up the database. It removes old read articles which are not starred Status : Implemented in News 1.601 Authentication : Requires admin user Method : GET Route : /cleanup/after-update Returns : Nothing New in 8.1.0 : The console command for achieving the same result is: php -f nextcloud/occ news:updater:after-update","title":"Trigger cleanup after update"},{"location":"api/api-v1-3/#version","text":"","title":"Version"},{"location":"api/api-v1-3/#get-the-version","text":"Status : Implemented Method : GET Route : /version Parameters : none Returns : { \"version\": \"5.2.3\" }","title":"Get the version"},{"location":"api/api-v1-3/#status","text":"This API can be used to display warnings and errors in your client if the web app is improperly configured or not working. It is a good idea to call this route on like every 10th update and after the server connection parameters have been changed since it's likely that the user set up a new instance and configured the app improperly.","title":"Status"},{"location":"api/api-v1-3/#get-the-status","text":"Status : Implemented in News 5.2.4 Method : GET Route : /status Parameters : none Returns : { \"version\": \"5.2.4\", \"warnings\": { \"improperlyConfiguredCron\": false, // if true the webapp will fail to update the feeds correctly \"incorrectDbCharset\": false } } If improperlyConfiguredCron is true you should display a warning that the app will not receive updates properly. This is due to the fact that the installation runs the cron in ajax mode to update the feeds. This is the default if you don't change anything and means that the app will only receive feed updates if the webinterface is accessed which will lead to lost updates. You should show the following warning and the link should be clickable: The News App updater is improperly configured and you will lose updates. See http://hisdomain.com/index.php/apps/news for instructions on how to fix it. If incorrectDbCharset is true you should display a warning that database charset is set up incorrectly and updates with unicode characters might fail","title":"Get the status"},{"location":"api/api-v1-3/#user","text":"This API can be used to retrieve metadata about the current user. DEPRECATED: This API is deprecated, use the Nextcloud APIs instead. - https://docs.nextcloud.com/server/latest/developer_manual/client_apis/OCS/ocs-api-overview.html#user-metadata for user data - https://nc.url/avatar/{userid}/{size}?v={1|2} for the avatar","title":"User"},{"location":"api/api-v1-3/#get-the-status_1","text":"Status : Implemented in News 6.0.5 Method : GET Route : /user Parameters : none Returns : { \"userId\": \"john\", \"displayName\": \"John Doe\", \"lastLoginTimestamp\": 1241231233, // unix timestamp \"avatar\": { // if no avatar exists, this is null \"data\": \"asdiufadfasdfjlkjlkjljdfdf\", // base64 encoded image \"mime\": \"image/jpeg\" } }","title":"Get the status"},{"location":"api/api-v2/","text":"External API v2 (Draft) Disclaimer: this API has not been fully implemented yet, help is welcome. The News app offers a RESTful API which can be used to sync folders, feeds and items. The API also supports CORS which means that you can access the API from your browser using JavaScript. In addition, an updater API is exposed which enables API users to run feed updates in parallel using a REST API or Nextcloud console API. Conventions This document uses the following conventions: Object aliases as comments Error objects are omitted Object Aliases As Comments In order to only specify the JSON objects once, comments are used to alias them. There are two types of aliases: Objects Object arrays Objects : { \"folder\": { /* folder object */ }, } means that the folder attributes will be listed inside the folder object Object arrays : { \"folders\": [ /* array of folder objects */ ], } means that folder objects will be listed inside the folders array. Error Objects Are Omitted This means that the error object will not be explicitly shown in the examples. All HTTP 400 response status codes contain an error object: { \"error\": { \"code\": 1, \"message\": \"error message\" } } API Stability Contract The API level will change if the following occurs: a required HTTP request header is added a required request parameter is added a field of a response object is removed a field of a response object is changed to a different datatype an HTTP response header is removed an HTTP response header is changed to a different datatype the meaning of an API call changes (e.g. /sync will not sync any more but show a sync timestamp) The API level will not change if: a new HTTP response header is added an optional new HTTP request header is added a new response parameter 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 Request Format The base URL for all calls is: https://yournextcloud.com/index.php/apps/news/api/v2 Unless an absolute Url is specified, the relative Urls in the Specification are appended to this url. To access the route /sync for instance you'd use the following url: https://yournextcloud.com/index.php/apps/news/api/v2/sync The required request headers are: Accept : application/json Any request method except GET: Content-Type : application/json; charset=utf-8 Any route that allows caching: If-None-Match : an Etag, e.g. 6d82cbb050ddc7fa9cbb659014546e59. If no previous Etag is known, this header should be omitted The request body is either passed in the URL in case of a GET request (e.g.: ?foo=bar&index=0 ) or as JSON, e.g.: { \"foo\": \"bar\", \"index\": 0 } Note : The current Etag implementation contains a unix timestamp in milliseconds. This is an implementation detail and you should not rely on it. API Level Detection Check the API level route Authentication Because REST is stateless you have to re-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 . Credentials are passed as an HTTP header using HTTP basic auth : Authorization: Basic $CREDENTIALS where $CREDENTIALS is: base64(USER:PASSWORD) This authentication/authorization method will be the recommended default until core provides an easy way to do OAuth Note : Even if login cookies are sent back to your client, they will not be considered for authentication. Response Format The status codes are not always provided by the News app itself, but might also be returned because of Nextcloud internal errors. The following status codes can always be returned by Nextcloud: 401 : The provided credentials to log into Nextcloud are invalid. 403 : The user is not allowed to access the route. This can happen if for instance of only users in the admin group can access the route and the user is not in it. 404 : The route can not be found or the resource does not exist. Can also happen if for instance you are trying to delete a folder which does not exist. 5xx : An internal server error occurred. This can happen if the server is in maintenance mode or because of other reasons. The following status codes are returned by News: 200 : Everything went fine 304 : In case the resource was not modified, contains no response body. This means that you can ignore the request since everything is up to date. 400 : There was an app related error, check the error object if specified 409 : Conflict error which means that the resource exists already. Can be returned when updating ( PATCH ) or creating ( POST ) a resource, e.g. a folder The response headers are: Content-Type : application/json; charset=utf-8 Etag : A string containing a cache header of maximum length 64, e.g. 6d82cbb050ddc7fa9cbb659014546e59. The etag value will be assembled using the number of feeds, folders and the highest last modified timestamp in milliseconds, e.g. 2-3-123131923912392391239. However consider that a detail and dont rely on it. The response body is a JSON structure that looks like this, which contains the actual data on the first level. The key is the resource in singular if it's a single resource or plural if its a collection. In case of HTTP 400, an error object is also present to help distinguishing between different error types: { \"error\": { \"code\": 1, \"message\": \"error message\" } } error : Only present when an HTTP 400 is returned to help distinguishing between error causes code : A unique error code message : A translated error message. The user's configured locale is used. In case of an 4xx or 5xx error the request was not successful and has to be retried. For instance marking items as read locally and syncing should send the same request again the next time the user syncs in case an error occurred. Security Guidelines Read the following notes carefully to prevent being subject to security exploits: You should always enforce SSL certificate verification and never offer a way to turn it off. Certificate verification is important to prevent MITM attacks which is especially important in the mobile world where users are almost always connected to untrusted networks. In case a user runs a self-signed certificate on his server ask him to either install his certificate on his device or direct him to one of the many ways to sign his certificate for free (most notably letsencrypt.com) All string fields in a JSON response expect an item's body are not sanitized . This means that if you do not escape it properly before rendering you will be vulnerable to XSS attacks Basic Auth headers can easily be decrypted by anyone since base64 is an encoding, not an encryption. Therefore only send them if you are accessing an HTTPS website or display an easy to understand warning if the user chooses HTTP When creating a feed you can choose to add basic auth authentication credentials. These must be stored in clear text so anyone with access to your database (however they might have achieved it, think of Sql injection) can read them and use them to access the website. You should warn the user about this. If you are building a client in JavaScript or are using a link with target=\"blank\" , remember to set the window.opener property to null and/or add a rel=\"noreferrer\" to your link to prevent your app from being target by an XSS attack Syncing All routes are given relative to the base API url, e.g.: /sync becomes https://yourNextcloud.com/index.php/apps/news/api/v2/sync There are two usecases for syncing: Initial sync : the user does not have any data at all Syncing local and remote changes : the user has synced at least once and wants to submit and receive changes Initial Sync The intial sync happens when a user adds an Nextcloud account in your app. In that case you want to download all folders, feeds and unread/starred items. To do this, make the following request: Method : GET Route : /sync Authentication : required HTTP headers : Accept: \"application/json\" This will return the following status codes: 200 : Success and the following HTTP headers: Content-Type : application/json; charset=utf-8 Etag : A string containing a cache header, maximum size 64 ASCII characters, e.g. 6d82cbb050ddc7fa9cbb659014546e59 and the following request body: { \"folders\": [ /* array of folder objects */ ], \"feeds\": [ /* array of feed objects */ ], \"items\": [ /* array of item objects */ ] } Note : Each object is explained in more detail in a separate section: Folders Feeds Items Sync Local And Remote Changes After the initial sync the app has all folders, feeds and items. Now you want to push changes and retrieve updates from the server. To do this, make the following request: Method : POST Route : /sync Authentication : required HTTP headers : Content-Type: \"application/json; charset=utf-8\" Accept: \"application/json\" If-None-Match: \"6d82cbb050ddc7fa9cbb659014546e59\" (Etag from the previous request to the /sync route) with the following request body: { \"items\": [{ // read and starred \"id\": 5, \"isStarred\": false, \"isUnread\": true, \"contentHash\": \"08ffbcf94bd95a1faa6e9e799cc29054\" }, { // only read \"id\": 6, \"isUnread\": true, \"contentHash\": \"09ffbcf94bd95a1faa6e9e799cc29054\" }, { // only starred \"id\": 7, \"isStarred\": false, \"contentHash\": \"18ffbcf94bd95a1faa6e9e799cc29054\" }, /* etc */] } If no items have been read or starred, simply leave the items array empty, e.g.: { \"items\": [] } The response matches the GET call, except there can be two different types of item objects: Full : Contains all attributes Reduced : Contains only id , isUnread and isStarred The deciding factor whether a full or reduced item object is being returned depends on the contentHash in the request: If the contentHash matches the record in the database a reduced item object is being returned, otherwise a full object is used. Both can occur in the same items array at the same time. The idea behind this special handling is that if the contentHash matches the record in the database, the actual item content did not change. Therefore it is enough to know the item status. This greatly reduces the amount sent over the Net which is especially important for mobile apps. This also applies to folders and feeds, however the reduced folder and feed objects will only include the id element. The deciding factor whether only an id or the full object will be returned is the last modified modified timestamp which is included in the sent etag. If you push a list of items to be marked read/starred, there can also be less items in the response than the ones which were initially sent. This means that the item was deleted by the cleanup job and should be removed from the client device. For instance let's take a look at the following example. You are POST ing the following JSON: { \"items\": [{ \"id\": 5, \"isStarred\": false, \"isUnread\": true, \"contentHash\": \"08ffbcf94bd95a1faa6e9e799cc29054\" }, { \"id\": 6, \"isUnread\": true, \"contentHash\": \"09ffbcf94bd95a1faa6e9e799cc29054\" }, { \"id\": 7, \"isStarred\": false, \"contentHash\": \"18ffbcf94bd95a1faa6e9e799cc29054\" }] } and receive the following output in return: { \"items\": [{ \"id\": 5, \"isStarred\": false, \"isUnread\": true }, { \"id\": 6, \"isUnread\": true, \"isStarred\": false }] } The item with the id 7 is missing from the response. This means that it was deleted on the server. For folders and feeds all ids will be returned so you can compare the existing ids with your locally available feeds and folders and remove the difference. Folders Folders are represented using the following data structure: { \"id\": 3, \"name\": \"funny stuff\" } The attributes mean the following: id : 64bit Integer, id name : Abitrary long text, folder's name Deleting A Folder To delete a folder, use the following request: Method : DELETE Route : /folders/{id} Route Parameters : {id} : folder's id Authentication : required The following response is being returned: Status codes: 200 : Folder was deleted successfully 404 : Folder does not exist In case of an HTTP 200, the deleted folder is returned in full in the response, e.g.: { \"folder\": { /* folder object */ } } Note : Deleted folders will not appear during the next sync so you also need to delete the folder locally afterwards. Folders should only be deleted locally if an HTTP 200 or 404 was returned. Note : If you delete a folder locally, you should also delete all feeds whose folderId attribute matches the folder's id attribute and also delete all items whose feedId attribute matches the feeds' id attribute. This is done automatically on the server and will also be missing on the next request. Creating A Folder To create a folder, use the following request: Method : POST Route : /folders Authentication : required with the following request body: { \"name\": \"Folder name\" } The following response is being returned: Status codes: 200 : Folder was created successfully 400 : Folder creation error, check the error object: code : 1: folder name is empty In case of an HTTP 200, the created or already existing folder is returned in full in the response, e.g.: { \"folder\": { /* folder object */ } } Changing A Folder The following attributes can be changed on the folder: name To change any number of attributes on a folder, use the following request and provide as much attributes that can be changed as you want: Method : PATCH Route : /folders/{id} Route Parameters : {id} : folder's id Authentication : required with the following request body: { \"name\": \"New folder name\" } name : Abitrary long text, the folder's name The following response is being returned: Status codes: 200 : Folder was updated successfully 400 : Folder update error, check the error object: code : 1: folder name is empty Other Nextcloud errors, see Response Format In case of an HTTP 200, the changed or already existing folder is returned in full in the response, e.g.: { \"folder\": { /* folder object */ } } Feeds Feeds are represented using the following data structure: { \"id\": 4, \"name\": \"The Oatmeal - Comics, Quizzes, & Stories\", \"faviconLink\": \"http://theoatmeal.com/favicon.ico\", \"folderId\": 3, \"ordering\": 0, \"fullTextEnabled\": false, \"updateMode\": 0, \"isPinned\": true, \"error\": { \"code\": 1, \"message\": \"\" } } The attributes mean the following: id : 64bit Integer, id name : Abitrary long text, feed's name faviconLink : Abitrary long text, feed's favicon location, null if not found folderId : 64bit Integer, the feed's folder or 0 in case no folder is specified ordering : 64bit Integer, overrides the feed's default ordering: 0 : Default 1 : Oldest on top 2 : Newest on top updateMode : 64bit Integer, describing how item updates are handled: 0 : No special behavior 1 : If an item is updated, mark it unread isPinned : Boolean, Used to list certain feeds before others. Feeds are first ordered by their isPinned value (true before false) and then by their name in alphabetical order error : error object, only present if an error occurred: code : The error code: 1 : Error occured during feed update message : Translated error message depending on the user's configured server locale Deleting A Feed To delete a feed, use the following request: Method : DELETE Route : /feeds/{id} Route Parameters : {id} : feed's id Authentication : required The following response is being returned: Status codes: 200 : Feed was deleted successfully 404 : Feed with given id was not found, no error object Other Nextcloud errors, see Response Format In case of an HTTP 200, the deleted feed is returned in full in the response, e.g.: { \"feed\": { /* feed object */ } } Note : Deleted feeds will not appear during the next sync so you also need to delete the feed locally afterwards. Feeds should only be deleted locally if an HTTP 200 or 404 was returned. Note : If you delete a feed locally, you should also delete all items whose feedId attribute matches the feeds' id attribute. This is done automatically on the server and will also be missing on the next request. Creating A feed To create a feed, use the following request: Method : POST Route : /feeds Authentication : required with the following request body: { \"url\": \"https://feed.url.com\", \"name\": \"Feed name\", \"ordering\": 0, \"folderId\": 3, \"isPinned\": true, \"fullTextEnabled\": false, \"basicAuthUser\": \"user\", \"basicAuthPassword\": \"password\" } url : Abitrary long text, the url needs to have the full schema e.g. https://the-url.com. In case the user omits the schema, prepending https is recommended folderId : 64bit Integer, the feed's folder or 0 in case no folder is specified name (optional) : Abitrary long text, the feeds name or if not given taken from the RSS/Atom feed basicAuthUser (optional) : Abitrary long text, if given basic auth headers are sent for the feed basicAuthPassword (optional) : Abitrary long text, if given basic auth headers are sent for the feed ordering (optional) : See Feeds isPinned (optional) : See Feeds fullTextEnabled (optional) : See Feeds The following response is being returned: Status codes: 200 : Feed was created successfully 400 : Feed creation error, check the error object: code : 1: url is empty code : 2: malformed xml code : 3: no feed found for url (e.g. website does not have an RSS or Atom feed or direct link to feed is no feed) code : 4: feed format not supported (e.g. too old RSS version) code : 5: ssl issues (e.g. SSL certificate is invalid or php has issues accessing certificates on your server) code : 6: url can not be found or accessed code : 7: maximum redirects reached code : 8: maximum size reached code : 9: request timed out code : 10: invalid or missing http basic auth headers code : 11: not allowed to access the feed (difference here is that the user can be authenticated but not allowed to access the feed) In case of an HTTP 200, the created feed is returned in full in the response, e.g.: { \"feed\": { /* feed object */ } } Note : Because the next sync would also pull in the added feed and items again, the added items will be omitted for saving bandwidth. This also means that after successfully creating a feed you will need to query the sync route again. Changing A Feed To change a feed, use the following request: Method : PATCH Route : /feeds/{id} Route Parameters : {id} : feed's id Authentication : required with the following request body: { \"url\": \"https://feed.url.com\", \"name\": \"Feed name\", \"ordering\": 0, \"folderId\": 3, \"isPinned\": true, \"fullTextEnabled\": false, \"basicAuthUser\": \"user\", \"basicAuthPassword\": \"password\" } All parameters are optional url (optional) : Abitrary long text, the url which was entered by the user with the full schema name (optional) : Abitrary long text, the feeds name or if not given taken from the RSS/Atom feed basicAuthUser (optional) : Abitrary long text, if given basic auth headers are sent for the feed basicAuthPassword (optional) : Abitrary long text, if given basic auth headers are sent for the feed ordering (optional) : See feeds isPinned (optional) : See feeds fullTextEnabled (optional) : See feeds folderId (optional) : 64bit Integer, the feed's folder or 0 in case no folder is specified The following response is being returned: Status codes: 200 : Feed was changed successfully 400 : Feed creation error, check the error object: code : 1: url is empty code : 2: malformed xml code : 3: no feed found for url (e.g. website does not have an RSS or Atom feed or direct link to feed is no feed) code : 4: feed format not supported (e.g. too old RSS version) code : 5: ssl issues (e.g. SSL certificate is invalid or php has issues accessing certificates on your server) code : 6: url can not be found or accessed code : 7: maximum redirects reached code : 8: maximum size reached code : 9: request timed out code : 10: invalid or missing http basic auth headers code : 11: not allowed to access the feed (difference here is that the user can be authenticated but not allowed to access the feed) Other Nextcloud errors, see Response Format In case of an HTTP 200, the changed feed is returned in full in the response, e.g.: { \"feed\": { /* feed object */ } } Note : Because the next sync would also pull in the changed feed and items again, the added or updated items will be omitted for saving bandwidth. This also means that after successfully updating a feed you will need to query the sync route again. Items Items can occur in two different formats: Full Reduced The attributes mean the following: id : 64bit Integer, id url : Abitrary long text, location of the online resource title : Abitrary long text, item's title author : Abitrary long text, name of the author/authors publishedAt : String representing an ISO 8601 DateTime object, when the item was published lastModifiedAt : String representing an ISO 8601 DateTime object, when the item was last modified enclosure : An enclosure object or null if none is present mimeType : Abitrary long text, the enclosures mime type url : Abitrary long text, location of the enclosure body : Abitrary long text, sanitized (meaning: does not have to be escape) , contains the item's content feedId : 64bit Integer, the item's feed it belongs to isUnread : Boolean, true if unread, false if read isStarred : Boolean, true if starred, false if not starred fingerprint : 64 ASCII characters, hash that is used to determine if an item is the same as an other one. The following behavior should be implemented: Items in a stream (e.g. All items, folders, feeds) should be filtered so that no item with the same fingerprint is present. When marking an item read, all items with the same fingerprint should also be marked as read. contentHash : 64 ASCII characters, used to determine if the item on the client is up to or out of date. The difference between the contentHash and the fingerprint attribute is that contentHash is always calculated from a stable set of attributes (title, author, url, enclosure, body) whereas the fingerprint is calculated from a set of attributes depending on the feed. The reason for this is that some feeds use different URLs for the same article so you would not want to include the URL as uniqueness criteria in that case. If the fingerprint was used for syncing however, an URL update would never reach the client. Full A full item contains the full content: { \"id\": 5, \"url\": \"http://grulja.wordpress.com/2013/04/29/plasma-nm-after-the-solid-sprint/\", \"title\": \"Plasma-nm after the solid sprint\", \"author\": \"Jan Grulich (grulja)\", \"publishedAt\": \"2005-08-15T15:52:01+0000\", \"lastModifiedAt\": \"2005-08-15T15:52:01+0000\", \"enclosure\": { \"mimeType\": \"video/webm\", \"url\": \"http://video.webmfiles.org/elephants-dream.webm\" }, \"body\": \"<p>At first I have to say...</p>\", \"feedId\": 4, \"isUnread\": true, \"isStarred\": true, \"fingerprint\": \"08ffbcf94bd95a1faa6e9e799cc29054\", \"contentHash\": \"18ffbcf94bd95a1faa6e9e799cc29054\" } Reduced A reduced item only contains the item status: { \"id\": 5, \"isUnread\": true, \"isStarred\": true } Updater Instead of using the built in, slow cron updater you can use the parallel update API to update feeds. The API can be accessed through REST or Nextcloud console API. The API should be used in the following way: Clean up before the update Get all feeds and user ids For each feed and user id, run the update Clean up after the update The reference implementation in Python should give you a good idea how to design your own updater. If the REST API is used, Authorization is required via Basic Auth and the user needs to be in the admin group. If the Nextcloud console API is used, no authorization is required. Clean Up Before Update This is used to clean up the database. It deletes folders and feeds that are marked for deletion. Console API : php -f /path/to/nextcloud/occ news:updater:before-update REST API : Method : GET Route : /updater/before-update Authentication : admin Get All Feeds And User Ids This call returns pairs of feed ids and user ids. Console API : php -f /path/to/nextcloud/occ news:updater:all-feeds REST API : Method : GET Route : /updater/all-feeds Authentication : admin Both APIs will return the following response body or terminal output: { \"updater\": [{ \"feedId\": 3, \"userId\": \"john\" }, /* etc */] } Update A User's Feed After all feed ids and user ids are known, feeds can be updated in parallel. Console API : Positional Parameters : {feedId} : the feed's id {userId} : the user's id php -f /path/to/nextcloud/occ news:updater:update-feed {feedId} {userId} REST API : Method : GET Route : /updater/update-feed?feedId={feedId}&userId={userId} Route Parameters : {feedId} : the feed's id {userId} : the user's id Authentication : admin Clean Up After Update This is used to clean up the database. It removes old read articles which are not starred. Console API : php -f /path/to/nextcloud/occ news:updater:after-update REST API : Method : GET Route : /updater/after-update Authentication : admin Meta Data The retrieve meta data about the app, use the following request: Method : GET Route : / Authentication : required The following response is being returned: Status codes: 200 : Meta data accessed successfully In case of an HTTP 200, the the following response is returned: { \"version\": \"9.0.0\", \"issues\": { \"improperlyConfiguredCron\": false }, \"user\": { \"userId\": \"john\", \"displayName\": \"John Doe\", \"avatar\": { \"data\": \"asdiufadfasdfjlkjlkjljdfdf\", \"mime\": \"image/jpeg\" } } } The attributes mean the following: version : Abitrary long text, News app version issues : An object containing a dictionary of issues which need to be displayed to the user: improperlyConfiguredCron : Boolean, if true this means that no feed updates are run on the server because the updater is misconfigured user : user information: userId : Abitrary long text, the login name displayName : Abitrary long text, the full name like it's displayed in the web interface avatar : an avatar object, null if none is set data : Abitrary long text, the user's image encoded as base64 mime : Abitrary long text, avatar mimetype API Level To find out which API levels are supported, make a request to the following route: Method : GET Route : https://yournextcloud.com/index.php/apps/news/api Authentication : none The following response is being returned: Status codes: 200 : The supported API levels can be parsed from the response 404 : The user is either running a version prior to 8.8.0 or the News app is disabled or not installed. In case of an HTTP 200, the supported API levels are returned as JSON, e.g.: { \"apiLevels\": [\"v1-2\", \"v2\"] } apiLevels : An array of arbitrary long strings, strings represent the the supported api levels which directly correspond to the first fragment after the /api/ Url fragment. To find out if a user is running an older News version than 8.8.0 , make a request to the following route: Method : GET Route : https://yournextcloud.com/index.php/apps/news/api/v1-2/version Authentication : required Status codes: 200 : Only the v1-2 API level is supported 404 : The News app is disabled or not installed. Since these calls can be expensive you could first try to make a call to the sync route and if it fails with an HTTP 404 run the API level detection. Of course the choice which APIs you are going to support is entirely yours and you could also hard require v2. Note : Future News app versions may remove the v1-2 API level depending on how widespread the adoption has become. You should therefore always make sure which API levels are actually supported.","title":"API v2"},{"location":"api/api-v2/#external-api-v2-draft","text":"Disclaimer: this API has not been fully implemented yet, help is welcome. The News app offers a RESTful API which can be used to sync folders, feeds and items. The API also supports CORS which means that you can access the API from your browser using JavaScript. In addition, an updater API is exposed which enables API users to run feed updates in parallel using a REST API or Nextcloud console API.","title":"External API v2 (Draft)"},{"location":"api/api-v2/#conventions","text":"This document uses the following conventions: Object aliases as comments Error objects are omitted","title":"Conventions"},{"location":"api/api-v2/#object-aliases-as-comments","text":"In order to only specify the JSON objects once, comments are used to alias them. There are two types of aliases: Objects Object arrays Objects : { \"folder\": { /* folder object */ }, } means that the folder attributes will be listed inside the folder object Object arrays : { \"folders\": [ /* array of folder objects */ ], } means that folder objects will be listed inside the folders array.","title":"Object Aliases As Comments"},{"location":"api/api-v2/#error-objects-are-omitted","text":"This means that the error object will not be explicitly shown in the examples. All HTTP 400 response status codes contain an error object: { \"error\": { \"code\": 1, \"message\": \"error message\" } }","title":"Error Objects Are Omitted"},{"location":"api/api-v2/#api-stability-contract","text":"The API level will change if the following occurs: a required HTTP request header is added a required request parameter is added a field of a response object is removed a field of a response object is changed to a different datatype an HTTP response header is removed an HTTP response header is changed to a different datatype the meaning of an API call changes (e.g. /sync will not sync any more but show a sync timestamp) The API level will not change if: a new HTTP response header is added an optional new HTTP request header is added a new response parameter 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","title":"API Stability Contract"},{"location":"api/api-v2/#request-format","text":"The base URL for all calls is: https://yournextcloud.com/index.php/apps/news/api/v2 Unless an absolute Url is specified, the relative Urls in the Specification are appended to this url. To access the route /sync for instance you'd use the following url: https://yournextcloud.com/index.php/apps/news/api/v2/sync The required request headers are: Accept : application/json Any request method except GET: Content-Type : application/json; charset=utf-8 Any route that allows caching: If-None-Match : an Etag, e.g. 6d82cbb050ddc7fa9cbb659014546e59. If no previous Etag is known, this header should be omitted The request body is either passed in the URL in case of a GET request (e.g.: ?foo=bar&index=0 ) or as JSON, e.g.: { \"foo\": \"bar\", \"index\": 0 } Note : The current Etag implementation contains a unix timestamp in milliseconds. This is an implementation detail and you should not rely on it.","title":"Request Format"},{"location":"api/api-v2/#api-level-detection","text":"Check the API level route","title":"API Level Detection"},{"location":"api/api-v2/#authentication","text":"Because REST is stateless you have to re-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 . Credentials are passed as an HTTP header using HTTP basic auth : Authorization: Basic $CREDENTIALS where $CREDENTIALS is: base64(USER:PASSWORD) This authentication/authorization method will be the recommended default until core provides an easy way to do OAuth Note : Even if login cookies are sent back to your client, they will not be considered for authentication.","title":"Authentication"},{"location":"api/api-v2/#response-format","text":"The status codes are not always provided by the News app itself, but might also be returned because of Nextcloud internal errors. The following status codes can always be returned by Nextcloud: 401 : The provided credentials to log into Nextcloud are invalid. 403 : The user is not allowed to access the route. This can happen if for instance of only users in the admin group can access the route and the user is not in it. 404 : The route can not be found or the resource does not exist. Can also happen if for instance you are trying to delete a folder which does not exist. 5xx : An internal server error occurred. This can happen if the server is in maintenance mode or because of other reasons. The following status codes are returned by News: 200 : Everything went fine 304 : In case the resource was not modified, contains no response body. This means that you can ignore the request since everything is up to date. 400 : There was an app related error, check the error object if specified 409 : Conflict error which means that the resource exists already. Can be returned when updating ( PATCH ) or creating ( POST ) a resource, e.g. a folder The response headers are: Content-Type : application/json; charset=utf-8 Etag : A string containing a cache header of maximum length 64, e.g. 6d82cbb050ddc7fa9cbb659014546e59. The etag value will be assembled using the number of feeds, folders and the highest last modified timestamp in milliseconds, e.g. 2-3-123131923912392391239. However consider that a detail and dont rely on it. The response body is a JSON structure that looks like this, which contains the actual data on the first level. The key is the resource in singular if it's a single resource or plural if its a collection. In case of HTTP 400, an error object is also present to help distinguishing between different error types: { \"error\": { \"code\": 1, \"message\": \"error message\" } } error : Only present when an HTTP 400 is returned to help distinguishing between error causes code : A unique error code message : A translated error message. The user's configured locale is used. In case of an 4xx or 5xx error the request was not successful and has to be retried. For instance marking items as read locally and syncing should send the same request again the next time the user syncs in case an error occurred.","title":"Response Format"},{"location":"api/api-v2/#security-guidelines","text":"Read the following notes carefully to prevent being subject to security exploits: You should always enforce SSL certificate verification and never offer a way to turn it off. Certificate verification is important to prevent MITM attacks which is especially important in the mobile world where users are almost always connected to untrusted networks. In case a user runs a self-signed certificate on his server ask him to either install his certificate on his device or direct him to one of the many ways to sign his certificate for free (most notably letsencrypt.com) All string fields in a JSON response expect an item's body are not sanitized . This means that if you do not escape it properly before rendering you will be vulnerable to XSS attacks Basic Auth headers can easily be decrypted by anyone since base64 is an encoding, not an encryption. Therefore only send them if you are accessing an HTTPS website or display an easy to understand warning if the user chooses HTTP When creating a feed you can choose to add basic auth authentication credentials. These must be stored in clear text so anyone with access to your database (however they might have achieved it, think of Sql injection) can read them and use them to access the website. You should warn the user about this. If you are building a client in JavaScript or are using a link with target=\"blank\" , remember to set the window.opener property to null and/or add a rel=\"noreferrer\" to your link to prevent your app from being target by an XSS attack","title":"Security Guidelines"},{"location":"api/api-v2/#syncing","text":"All routes are given relative to the base API url, e.g.: /sync becomes https://yourNextcloud.com/index.php/apps/news/api/v2/sync There are two usecases for syncing: Initial sync : the user does not have any data at all Syncing local and remote changes : the user has synced at least once and wants to submit and receive changes","title":"Syncing"},{"location":"api/api-v2/#initial-sync","text":"The intial sync happens when a user adds an Nextcloud account in your app. In that case you want to download all folders, feeds and unread/starred items. To do this, make the following request: Method : GET Route : /sync Authentication : required HTTP headers : Accept: \"application/json\" This will return the following status codes: 200 : Success and the following HTTP headers: Content-Type : application/json; charset=utf-8 Etag : A string containing a cache header, maximum size 64 ASCII characters, e.g. 6d82cbb050ddc7fa9cbb659014546e59 and the following request body: { \"folders\": [ /* array of folder objects */ ], \"feeds\": [ /* array of feed objects */ ], \"items\": [ /* array of item objects */ ] } Note : Each object is explained in more detail in a separate section: Folders Feeds Items","title":"Initial Sync"},{"location":"api/api-v2/#sync-local-and-remote-changes","text":"After the initial sync the app has all folders, feeds and items. Now you want to push changes and retrieve updates from the server. To do this, make the following request: Method : POST Route : /sync Authentication : required HTTP headers : Content-Type: \"application/json; charset=utf-8\" Accept: \"application/json\" If-None-Match: \"6d82cbb050ddc7fa9cbb659014546e59\" (Etag from the previous request to the /sync route) with the following request body: { \"items\": [{ // read and starred \"id\": 5, \"isStarred\": false, \"isUnread\": true, \"contentHash\": \"08ffbcf94bd95a1faa6e9e799cc29054\" }, { // only read \"id\": 6, \"isUnread\": true, \"contentHash\": \"09ffbcf94bd95a1faa6e9e799cc29054\" }, { // only starred \"id\": 7, \"isStarred\": false, \"contentHash\": \"18ffbcf94bd95a1faa6e9e799cc29054\" }, /* etc */] } If no items have been read or starred, simply leave the items array empty, e.g.: { \"items\": [] } The response matches the GET call, except there can be two different types of item objects: Full : Contains all attributes Reduced : Contains only id , isUnread and isStarred The deciding factor whether a full or reduced item object is being returned depends on the contentHash in the request: If the contentHash matches the record in the database a reduced item object is being returned, otherwise a full object is used. Both can occur in the same items array at the same time. The idea behind this special handling is that if the contentHash matches the record in the database, the actual item content did not change. Therefore it is enough to know the item status. This greatly reduces the amount sent over the Net which is especially important for mobile apps. This also applies to folders and feeds, however the reduced folder and feed objects will only include the id element. The deciding factor whether only an id or the full object will be returned is the last modified modified timestamp which is included in the sent etag. If you push a list of items to be marked read/starred, there can also be less items in the response than the ones which were initially sent. This means that the item was deleted by the cleanup job and should be removed from the client device. For instance let's take a look at the following example. You are POST ing the following JSON: { \"items\": [{ \"id\": 5, \"isStarred\": false, \"isUnread\": true, \"contentHash\": \"08ffbcf94bd95a1faa6e9e799cc29054\" }, { \"id\": 6, \"isUnread\": true, \"contentHash\": \"09ffbcf94bd95a1faa6e9e799cc29054\" }, { \"id\": 7, \"isStarred\": false, \"contentHash\": \"18ffbcf94bd95a1faa6e9e799cc29054\" }] } and receive the following output in return: { \"items\": [{ \"id\": 5, \"isStarred\": false, \"isUnread\": true }, { \"id\": 6, \"isUnread\": true, \"isStarred\": false }] } The item with the id 7 is missing from the response. This means that it was deleted on the server. For folders and feeds all ids will be returned so you can compare the existing ids with your locally available feeds and folders and remove the difference.","title":"Sync Local And Remote Changes"},{"location":"api/api-v2/#folders","text":"Folders are represented using the following data structure: { \"id\": 3, \"name\": \"funny stuff\" } The attributes mean the following: id : 64bit Integer, id name : Abitrary long text, folder's name","title":"Folders"},{"location":"api/api-v2/#deleting-a-folder","text":"To delete a folder, use the following request: Method : DELETE Route : /folders/{id} Route Parameters : {id} : folder's id Authentication : required The following response is being returned: Status codes: 200 : Folder was deleted successfully 404 : Folder does not exist In case of an HTTP 200, the deleted folder is returned in full in the response, e.g.: { \"folder\": { /* folder object */ } } Note : Deleted folders will not appear during the next sync so you also need to delete the folder locally afterwards. Folders should only be deleted locally if an HTTP 200 or 404 was returned. Note : If you delete a folder locally, you should also delete all feeds whose folderId attribute matches the folder's id attribute and also delete all items whose feedId attribute matches the feeds' id attribute. This is done automatically on the server and will also be missing on the next request.","title":"Deleting A Folder"},{"location":"api/api-v2/#creating-a-folder","text":"To create a folder, use the following request: Method : POST Route : /folders Authentication : required with the following request body: { \"name\": \"Folder name\" } The following response is being returned: Status codes: 200 : Folder was created successfully 400 : Folder creation error, check the error object: code : 1: folder name is empty In case of an HTTP 200, the created or already existing folder is returned in full in the response, e.g.: { \"folder\": { /* folder object */ } }","title":"Creating A Folder"},{"location":"api/api-v2/#changing-a-folder","text":"The following attributes can be changed on the folder: name To change any number of attributes on a folder, use the following request and provide as much attributes that can be changed as you want: Method : PATCH Route : /folders/{id} Route Parameters : {id} : folder's id Authentication : required with the following request body: { \"name\": \"New folder name\" } name : Abitrary long text, the folder's name The following response is being returned: Status codes: 200 : Folder was updated successfully 400 : Folder update error, check the error object: code : 1: folder name is empty Other Nextcloud errors, see Response Format In case of an HTTP 200, the changed or already existing folder is returned in full in the response, e.g.: { \"folder\": { /* folder object */ } }","title":"Changing A Folder"},{"location":"api/api-v2/#feeds","text":"Feeds are represented using the following data structure: { \"id\": 4, \"name\": \"The Oatmeal - Comics, Quizzes, & Stories\", \"faviconLink\": \"http://theoatmeal.com/favicon.ico\", \"folderId\": 3, \"ordering\": 0, \"fullTextEnabled\": false, \"updateMode\": 0, \"isPinned\": true, \"error\": { \"code\": 1, \"message\": \"\" } } The attributes mean the following: id : 64bit Integer, id name : Abitrary long text, feed's name faviconLink : Abitrary long text, feed's favicon location, null if not found folderId : 64bit Integer, the feed's folder or 0 in case no folder is specified ordering : 64bit Integer, overrides the feed's default ordering: 0 : Default 1 : Oldest on top 2 : Newest on top updateMode : 64bit Integer, describing how item updates are handled: 0 : No special behavior 1 : If an item is updated, mark it unread isPinned : Boolean, Used to list certain feeds before others. Feeds are first ordered by their isPinned value (true before false) and then by their name in alphabetical order error : error object, only present if an error occurred: code : The error code: 1 : Error occured during feed update message : Translated error message depending on the user's configured server locale","title":"Feeds"},{"location":"api/api-v2/#deleting-a-feed","text":"To delete a feed, use the following request: Method : DELETE Route : /feeds/{id} Route Parameters : {id} : feed's id Authentication : required The following response is being returned: Status codes: 200 : Feed was deleted successfully 404 : Feed with given id was not found, no error object Other Nextcloud errors, see Response Format In case of an HTTP 200, the deleted feed is returned in full in the response, e.g.: { \"feed\": { /* feed object */ } } Note : Deleted feeds will not appear during the next sync so you also need to delete the feed locally afterwards. Feeds should only be deleted locally if an HTTP 200 or 404 was returned. Note : If you delete a feed locally, you should also delete all items whose feedId attribute matches the feeds' id attribute. This is done automatically on the server and will also be missing on the next request.","title":"Deleting A Feed"},{"location":"api/api-v2/#creating-a-feed","text":"To create a feed, use the following request: Method : POST Route : /feeds Authentication : required with the following request body: { \"url\": \"https://feed.url.com\", \"name\": \"Feed name\", \"ordering\": 0, \"folderId\": 3, \"isPinned\": true, \"fullTextEnabled\": false, \"basicAuthUser\": \"user\", \"basicAuthPassword\": \"password\" } url : Abitrary long text, the url needs to have the full schema e.g. https://the-url.com. In case the user omits the schema, prepending https is recommended folderId : 64bit Integer, the feed's folder or 0 in case no folder is specified name (optional) : Abitrary long text, the feeds name or if not given taken from the RSS/Atom feed basicAuthUser (optional) : Abitrary long text, if given basic auth headers are sent for the feed basicAuthPassword (optional) : Abitrary long text, if given basic auth headers are sent for the feed ordering (optional) : See Feeds isPinned (optional) : See Feeds fullTextEnabled (optional) : See Feeds The following response is being returned: Status codes: 200 : Feed was created successfully 400 : Feed creation error, check the error object: code : 1: url is empty code : 2: malformed xml code : 3: no feed found for url (e.g. website does not have an RSS or Atom feed or direct link to feed is no feed) code : 4: feed format not supported (e.g. too old RSS version) code : 5: ssl issues (e.g. SSL certificate is invalid or php has issues accessing certificates on your server) code : 6: url can not be found or accessed code : 7: maximum redirects reached code : 8: maximum size reached code : 9: request timed out code : 10: invalid or missing http basic auth headers code : 11: not allowed to access the feed (difference here is that the user can be authenticated but not allowed to access the feed) In case of an HTTP 200, the created feed is returned in full in the response, e.g.: { \"feed\": { /* feed object */ } } Note : Because the next sync would also pull in the added feed and items again, the added items will be omitted for saving bandwidth. This also means that after successfully creating a feed you will need to query the sync route again.","title":"Creating A feed"},{"location":"api/api-v2/#changing-a-feed","text":"To change a feed, use the following request: Method : PATCH Route : /feeds/{id} Route Parameters : {id} : feed's id Authentication : required with the following request body: { \"url\": \"https://feed.url.com\", \"name\": \"Feed name\", \"ordering\": 0, \"folderId\": 3, \"isPinned\": true, \"fullTextEnabled\": false, \"basicAuthUser\": \"user\", \"basicAuthPassword\": \"password\" } All parameters are optional url (optional) : Abitrary long text, the url which was entered by the user with the full schema name (optional) : Abitrary long text, the feeds name or if not given taken from the RSS/Atom feed basicAuthUser (optional) : Abitrary long text, if given basic auth headers are sent for the feed basicAuthPassword (optional) : Abitrary long text, if given basic auth headers are sent for the feed ordering (optional) : See feeds isPinned (optional) : See feeds fullTextEnabled (optional) : See feeds folderId (optional) : 64bit Integer, the feed's folder or 0 in case no folder is specified The following response is being returned: Status codes: 200 : Feed was changed successfully 400 : Feed creation error, check the error object: code : 1: url is empty code : 2: malformed xml code : 3: no feed found for url (e.g. website does not have an RSS or Atom feed or direct link to feed is no feed) code : 4: feed format not supported (e.g. too old RSS version) code : 5: ssl issues (e.g. SSL certificate is invalid or php has issues accessing certificates on your server) code : 6: url can not be found or accessed code : 7: maximum redirects reached code : 8: maximum size reached code : 9: request timed out code : 10: invalid or missing http basic auth headers code : 11: not allowed to access the feed (difference here is that the user can be authenticated but not allowed to access the feed) Other Nextcloud errors, see Response Format In case of an HTTP 200, the changed feed is returned in full in the response, e.g.: { \"feed\": { /* feed object */ } } Note : Because the next sync would also pull in the changed feed and items again, the added or updated items will be omitted for saving bandwidth. This also means that after successfully updating a feed you will need to query the sync route again.","title":"Changing A Feed"},{"location":"api/api-v2/#items","text":"Items can occur in two different formats: Full Reduced The attributes mean the following: id : 64bit Integer, id url : Abitrary long text, location of the online resource title : Abitrary long text, item's title author : Abitrary long text, name of the author/authors publishedAt : String representing an ISO 8601 DateTime object, when the item was published lastModifiedAt : String representing an ISO 8601 DateTime object, when the item was last modified enclosure : An enclosure object or null if none is present mimeType : Abitrary long text, the enclosures mime type url : Abitrary long text, location of the enclosure body : Abitrary long text, sanitized (meaning: does not have to be escape) , contains the item's content feedId : 64bit Integer, the item's feed it belongs to isUnread : Boolean, true if unread, false if read isStarred : Boolean, true if starred, false if not starred fingerprint : 64 ASCII characters, hash that is used to determine if an item is the same as an other one. The following behavior should be implemented: Items in a stream (e.g. All items, folders, feeds) should be filtered so that no item with the same fingerprint is present. When marking an item read, all items with the same fingerprint should also be marked as read. contentHash : 64 ASCII characters, used to determine if the item on the client is up to or out of date. The difference between the contentHash and the fingerprint attribute is that contentHash is always calculated from a stable set of attributes (title, author, url, enclosure, body) whereas the fingerprint is calculated from a set of attributes depending on the feed. The reason for this is that some feeds use different URLs for the same article so you would not want to include the URL as uniqueness criteria in that case. If the fingerprint was used for syncing however, an URL update would never reach the client.","title":"Items"},{"location":"api/api-v2/#full","text":"A full item contains the full content: { \"id\": 5, \"url\": \"http://grulja.wordpress.com/2013/04/29/plasma-nm-after-the-solid-sprint/\", \"title\": \"Plasma-nm after the solid sprint\", \"author\": \"Jan Grulich (grulja)\", \"publishedAt\": \"2005-08-15T15:52:01+0000\", \"lastModifiedAt\": \"2005-08-15T15:52:01+0000\", \"enclosure\": { \"mimeType\": \"video/webm\", \"url\": \"http://video.webmfiles.org/elephants-dream.webm\" }, \"body\": \"<p>At first I have to say...</p>\", \"feedId\": 4, \"isUnread\": true, \"isStarred\": true, \"fingerprint\": \"08ffbcf94bd95a1faa6e9e799cc29054\", \"contentHash\": \"18ffbcf94bd95a1faa6e9e799cc29054\" }","title":"Full"},{"location":"api/api-v2/#reduced","text":"A reduced item only contains the item status: { \"id\": 5, \"isUnread\": true, \"isStarred\": true }","title":"Reduced"},{"location":"api/api-v2/#updater","text":"Instead of using the built in, slow cron updater you can use the parallel update API to update feeds. The API can be accessed through REST or Nextcloud console API. The API should be used in the following way: Clean up before the update Get all feeds and user ids For each feed and user id, run the update Clean up after the update The reference implementation in Python should give you a good idea how to design your own updater. If the REST API is used, Authorization is required via Basic Auth and the user needs to be in the admin group. If the Nextcloud console API is used, no authorization is required.","title":"Updater"},{"location":"api/api-v2/#clean-up-before-update","text":"This is used to clean up the database. It deletes folders and feeds that are marked for deletion. Console API : php -f /path/to/nextcloud/occ news:updater:before-update REST API : Method : GET Route : /updater/before-update Authentication : admin","title":"Clean Up Before Update"},{"location":"api/api-v2/#get-all-feeds-and-user-ids","text":"This call returns pairs of feed ids and user ids. Console API : php -f /path/to/nextcloud/occ news:updater:all-feeds REST API : Method : GET Route : /updater/all-feeds Authentication : admin Both APIs will return the following response body or terminal output: { \"updater\": [{ \"feedId\": 3, \"userId\": \"john\" }, /* etc */] }","title":"Get All Feeds And User Ids"},{"location":"api/api-v2/#update-a-users-feed","text":"After all feed ids and user ids are known, feeds can be updated in parallel. Console API : Positional Parameters : {feedId} : the feed's id {userId} : the user's id php -f /path/to/nextcloud/occ news:updater:update-feed {feedId} {userId} REST API : Method : GET Route : /updater/update-feed?feedId={feedId}&userId={userId} Route Parameters : {feedId} : the feed's id {userId} : the user's id Authentication : admin","title":"Update A User's Feed"},{"location":"api/api-v2/#clean-up-after-update","text":"This is used to clean up the database. It removes old read articles which are not starred. Console API : php -f /path/to/nextcloud/occ news:updater:after-update REST API : Method : GET Route : /updater/after-update Authentication : admin","title":"Clean Up After Update"},{"location":"api/api-v2/#meta-data","text":"The retrieve meta data about the app, use the following request: Method : GET Route : / Authentication : required The following response is being returned: Status codes: 200 : Meta data accessed successfully In case of an HTTP 200, the the following response is returned: { \"version\": \"9.0.0\", \"issues\": { \"improperlyConfiguredCron\": false }, \"user\": { \"userId\": \"john\", \"displayName\": \"John Doe\", \"avatar\": { \"data\": \"asdiufadfasdfjlkjlkjljdfdf\", \"mime\": \"image/jpeg\" } } } The attributes mean the following: version : Abitrary long text, News app version issues : An object containing a dictionary of issues which need to be displayed to the user: improperlyConfiguredCron : Boolean, if true this means that no feed updates are run on the server because the updater is misconfigured user : user information: userId : Abitrary long text, the login name displayName : Abitrary long text, the full name like it's displayed in the web interface avatar : an avatar object, null if none is set data : Abitrary long text, the user's image encoded as base64 mime : Abitrary long text, avatar mimetype","title":"Meta Data"},{"location":"api/api-v2/#api-level","text":"To find out which API levels are supported, make a request to the following route: Method : GET Route : https://yournextcloud.com/index.php/apps/news/api Authentication : none The following response is being returned: Status codes: 200 : The supported API levels can be parsed from the response 404 : The user is either running a version prior to 8.8.0 or the News app is disabled or not installed. In case of an HTTP 200, the supported API levels are returned as JSON, e.g.: { \"apiLevels\": [\"v1-2\", \"v2\"] } apiLevels : An array of arbitrary long strings, strings represent the the supported api levels which directly correspond to the first fragment after the /api/ Url fragment. To find out if a user is running an older News version than 8.8.0 , make a request to the following route: Method : GET Route : https://yournextcloud.com/index.php/apps/news/api/v1-2/version Authentication : required Status codes: 200 : Only the v1-2 API level is supported 404 : The News app is disabled or not installed. Since these calls can be expensive you could first try to make a call to the sync route and if it fails with an HTTP 404 run the API level detection. Of course the choice which APIs you are going to support is entirely yours and you could also hard require v2. Note : Future News app versions may remove the v1-2 API level depending on how widespread the adoption has become. You should therefore always make sure which API levels are actually supported.","title":"API Level"},{"location":"features/customCSS/","text":"Custom CSS Sometimes you want to add additional CSS for a feed to improve the rendering. This can very easily be done by adding a CSS class to css/custom.css following the following naming convention: Take the URL from the \\ attribute (e.g.: \\ https://www.google.de/path?my=query \\ ) Extract the Domain from the URL (e.g.: www.google.de) Strip the leading www. (e.g.: google.de) Replace all . with - (e.g.: google-de) Prepend custom- (e.g.: custom-google-de) Each class rule should be prefixed with #app-content and should only affect the article body. An example rule would be: #app-content .custom-google-de .body { /* Custom CSS rules here */ }","title":"Custom CSS"},{"location":"features/customCSS/#custom-css","text":"Sometimes you want to add additional CSS for a feed to improve the rendering. This can very easily be done by adding a CSS class to css/custom.css following the following naming convention: Take the URL from the \\ attribute (e.g.: \\ https://www.google.de/path?my=query \\ ) Extract the Domain from the URL (e.g.: www.google.de) Strip the leading www. (e.g.: google.de) Replace all . with - (e.g.: google-de) Prepend custom- (e.g.: custom-google-de) Each class rule should be prefixed with #app-content and should only affect the article body. An example rule would be: #app-content .custom-google-de .body { /* Custom CSS rules here */ }","title":"Custom CSS"},{"location":"features/plugins/","text":"Plugins Plugins were created to keep the app maintainable while still making it possible to easily implement additional functionality. There are essentially three different use cases for plugins: Creating or extending server-side functionality, e.g. creating additional REST API endpoints Offering article actions such as share via Twitter or E-Mail Dropping in additional CSS or JavaScript The Basics Whatever plugin you want to create, you first need to create a basic structure. A plugin is basically just an app, so you can take advantage of the full Nextcloud app API . If you want you can take a look at the developer docs or dig into the tutorial . However, if you just want to start slow, the full process is described below. First create the following directories and files: newsplugin/ appinfo/ app.php info.xml The first folder name affects the name and namespace of your plugin and only one app can exist using the same name. Choose wisely. First let's add some meta ata about our app. Open the newsplugin/appinfo/info.xml and add the following contents: <?xml version=\"1.0\"?> <info> <id>newsplugin</id> <name>Example News Plugin</name> <description>This plugin allows you to share articles via Twitter</description> <licence>AGPL</licence> <author>Your Name Here</author> <version>0.0.1</version> <dependencies> <nextcloud min-version=\"10\"/> <owncloud min-version=\"9.1\"/> <php min-version=\"5.6\"/> </dependencies> </info> Note : You must license your app under the AGPL 3 or later to comply with the News app's license. Don't forget to add the license as plain text file if you want to distribute your app! Then we want to make sure that our code is only run if the News app is enabled. To do that put the following PHP code into the newsplugin/appinfo/app.php file: <?php namespace OCA\\NewsPlugin\\AppInfo; use OCP\\App; if (App::isEnabled('news')) { // your code here } If your plugin integrates with another Nextcloud app, make sure to also require it be installed. If you depend on the Bookmarks app for instance use: <?php namespace OCA\\MyNewsPlugin\\AppInfo; use OCP\\App; if (App::isEnabled('news') && App::isEnabled('bookmarks')) { // your code here } Now you are ready to enable the app. Head over to the apps section and choose the Not enabled section. Your app should be listed under the name Example News Plugin (or whatever name you set in the info.xml ). With the basics set up, you can now choose how to progress further. In our case we just want to add some additional CSS and JavaScript, so we are going to create a client-side plugin. Client-Side Plugin A client-side plugin is adding additional JavaScript and/or CSS to the News app. Remember the app.php ? Open it and place the following contents inside: <?php namespace OCA\\MyNewsPlugin\\AppInfo; use OCP\\App; if (App::isEnabled('news') && class_exists('OCA\\News\\Plugin\\Client\\Plugin')) { \\OCA\\News\\Plugin\\Client\\Plugin::registerScript('newsplugin', 'script'); \\OCA\\News\\Plugin\\Client\\Plugin::registerStyle('newsplugin', 'style'); } This will tell the News app to load the following files after its own JavaScript and CSS files have been included: newsplugin/js/script.js newspluing/css/style.css Adding Basic JavaScript Functionality You can basically add any JavaScript you want. If you want to add a new article action, this is a bit more complicated because it's hard to hook into Angular from the outside. Therefore, the News app provides an API which makes creating additional article actions a breeze. A basic article action looks like this: News.addArticleAction(function($actionsElement, article) { // your code here }); The addArticleAction method expects a function with the following parameters: $actionsElement : The DOM element wrapped in jQuery where your plugin should be appended to article : The current article's data (readonly!). The article object has the following properties: id : the article ID in the News database url : the article URL it points to title : the article title author : the article author pubDate : the article published date, a Unix timestamp body : the HTML content enclosureMime : if an enclosure is added, this is the mime type enclosureLink : this is the source of the enclosure mediaThumbnail : if there is a media attached, this is its thumbnail mediaDescription : if there is a media attached, this is its description feedId : the feed ID it belongs to unread : if the article is unread (bool) starred : if the article is starred (bool) lastModified : the last modified date With that in mind, let's add the Twitter button. Open the JavaScript file at newsplugin/js/script.js and add the following contents: News.addArticleAction(function($actionsElement, article) { var $li = $('<li>') .addClass('article-plugin-twitter'); var $button = $('<button>') .attr('title', t('newsplugin', 'Share on Twitter')); var text = 'Read this: ' + article.url; var url = 'https://twitter.com/intent/tweet?text=' + encodeURIComponent(text); $button.click(function (event) { window.open(url); window.opener = null; // prevent twitter being from able to access the DOM event.stopPropagation(); // prevent expanding in compact mode }); $li.append($button); $actionsElement.append($li); }); Great! Now the only thing left is to add some styles. Adding Styles Now let's add some styles to our app. We want to style the button to look like a Twitter icon, so simply download an icon (e.g. from Wikipedia ) and place it at newsplugin/img/twitter.png . Then open the newspluing/css/style.css file and add the following CSS: .article-plugin-twitter button { background-image: url('../img/twitter.png'); } Reload the News app and click the three dots menu, sit back and enjoy :) Server-Side Plugin A Server-Side plugin is a plugin that uses the same infrastructure as the News app for its own purposes. An example would be a plugin that makes the starred entries of a user available via an interface or a bookmark app that also shows starred articles as bookmarks. It's very easy to interface with the News app. Because all Classes are registered in the news/app/application.php it takes almost no effort to use the same infrastructure. Note : Keep in mind that these classes are essentially private which means they might break if the News app changes. There is no real public API so use at your own risk ;) Since you don't want to extend the app but use its resources, its advised that you don't inherit from the Application class but rather include it in your own container in newsplugin/appinfo/application.php : <?php namespace OCA\\NewsPlugin\\AppInfo; use OCP\\AppFramework\\App; use OCA\\News\\AppInfo\\Application as News; class Application extends App { public function __construct (array $urlParams=[]) { parent::__construct('newsplugin', $urlParams); $container = $this->getContainer(); $container->registerService('NewsContainer', function($c) { $app = new News(); return $app->getContainer(); }); $container->registerService(OCA\\News\\Service\\FeedService::class, function($c) { // use the feed service from the news app, you can use all // defined classes but its recommended that you stick to the // mapper and service classes since they are less likely to change return $c->query('NewsContainer')->query(OCA\\News\\Service\\FeedService::class); }); } } Using automatic container assembly you can then use it from your code by simply adding the type to your constructors. Examples Client-side plugins: Mail Share : Client-side plugin to share articles by email Server-side plugins: Feed Central : Publish your feeds as RSS","title":"Plugins"},{"location":"features/plugins/#plugins","text":"Plugins were created to keep the app maintainable while still making it possible to easily implement additional functionality. There are essentially three different use cases for plugins: Creating or extending server-side functionality, e.g. creating additional REST API endpoints Offering article actions such as share via Twitter or E-Mail Dropping in additional CSS or JavaScript","title":"Plugins"},{"location":"features/plugins/#the-basics","text":"Whatever plugin you want to create, you first need to create a basic structure. A plugin is basically just an app, so you can take advantage of the full Nextcloud app API . If you want you can take a look at the developer docs or dig into the tutorial . However, if you just want to start slow, the full process is described below. First create the following directories and files: newsplugin/ appinfo/ app.php info.xml The first folder name affects the name and namespace of your plugin and only one app can exist using the same name. Choose wisely. First let's add some meta ata about our app. Open the newsplugin/appinfo/info.xml and add the following contents: <?xml version=\"1.0\"?> <info> <id>newsplugin</id> <name>Example News Plugin</name> <description>This plugin allows you to share articles via Twitter</description> <licence>AGPL</licence> <author>Your Name Here</author> <version>0.0.1</version> <dependencies> <nextcloud min-version=\"10\"/> <owncloud min-version=\"9.1\"/> <php min-version=\"5.6\"/> </dependencies> </info> Note : You must license your app under the AGPL 3 or later to comply with the News app's license. Don't forget to add the license as plain text file if you want to distribute your app! Then we want to make sure that our code is only run if the News app is enabled. To do that put the following PHP code into the newsplugin/appinfo/app.php file: <?php namespace OCA\\NewsPlugin\\AppInfo; use OCP\\App; if (App::isEnabled('news')) { // your code here } If your plugin integrates with another Nextcloud app, make sure to also require it be installed. If you depend on the Bookmarks app for instance use: <?php namespace OCA\\MyNewsPlugin\\AppInfo; use OCP\\App; if (App::isEnabled('news') && App::isEnabled('bookmarks')) { // your code here } Now you are ready to enable the app. Head over to the apps section and choose the Not enabled section. Your app should be listed under the name Example News Plugin (or whatever name you set in the info.xml ). With the basics set up, you can now choose how to progress further. In our case we just want to add some additional CSS and JavaScript, so we are going to create a client-side plugin.","title":"The Basics"},{"location":"features/plugins/#client-side-plugin","text":"A client-side plugin is adding additional JavaScript and/or CSS to the News app. Remember the app.php ? Open it and place the following contents inside: <?php namespace OCA\\MyNewsPlugin\\AppInfo; use OCP\\App; if (App::isEnabled('news') && class_exists('OCA\\News\\Plugin\\Client\\Plugin')) { \\OCA\\News\\Plugin\\Client\\Plugin::registerScript('newsplugin', 'script'); \\OCA\\News\\Plugin\\Client\\Plugin::registerStyle('newsplugin', 'style'); } This will tell the News app to load the following files after its own JavaScript and CSS files have been included: newsplugin/js/script.js newspluing/css/style.css","title":"Client-Side Plugin"},{"location":"features/plugins/#adding-basic-javascript-functionality","text":"You can basically add any JavaScript you want. If you want to add a new article action, this is a bit more complicated because it's hard to hook into Angular from the outside. Therefore, the News app provides an API which makes creating additional article actions a breeze. A basic article action looks like this: News.addArticleAction(function($actionsElement, article) { // your code here }); The addArticleAction method expects a function with the following parameters: $actionsElement : The DOM element wrapped in jQuery where your plugin should be appended to article : The current article's data (readonly!). The article object has the following properties: id : the article ID in the News database url : the article URL it points to title : the article title author : the article author pubDate : the article published date, a Unix timestamp body : the HTML content enclosureMime : if an enclosure is added, this is the mime type enclosureLink : this is the source of the enclosure mediaThumbnail : if there is a media attached, this is its thumbnail mediaDescription : if there is a media attached, this is its description feedId : the feed ID it belongs to unread : if the article is unread (bool) starred : if the article is starred (bool) lastModified : the last modified date With that in mind, let's add the Twitter button. Open the JavaScript file at newsplugin/js/script.js and add the following contents: News.addArticleAction(function($actionsElement, article) { var $li = $('<li>') .addClass('article-plugin-twitter'); var $button = $('<button>') .attr('title', t('newsplugin', 'Share on Twitter')); var text = 'Read this: ' + article.url; var url = 'https://twitter.com/intent/tweet?text=' + encodeURIComponent(text); $button.click(function (event) { window.open(url); window.opener = null; // prevent twitter being from able to access the DOM event.stopPropagation(); // prevent expanding in compact mode }); $li.append($button); $actionsElement.append($li); }); Great! Now the only thing left is to add some styles.","title":"Adding Basic JavaScript Functionality"},{"location":"features/plugins/#adding-styles","text":"Now let's add some styles to our app. We want to style the button to look like a Twitter icon, so simply download an icon (e.g. from Wikipedia ) and place it at newsplugin/img/twitter.png . Then open the newspluing/css/style.css file and add the following CSS: .article-plugin-twitter button { background-image: url('../img/twitter.png'); } Reload the News app and click the three dots menu, sit back and enjoy :)","title":"Adding Styles"},{"location":"features/plugins/#server-side-plugin","text":"A Server-Side plugin is a plugin that uses the same infrastructure as the News app for its own purposes. An example would be a plugin that makes the starred entries of a user available via an interface or a bookmark app that also shows starred articles as bookmarks. It's very easy to interface with the News app. Because all Classes are registered in the news/app/application.php it takes almost no effort to use the same infrastructure. Note : Keep in mind that these classes are essentially private which means they might break if the News app changes. There is no real public API so use at your own risk ;) Since you don't want to extend the app but use its resources, its advised that you don't inherit from the Application class but rather include it in your own container in newsplugin/appinfo/application.php : <?php namespace OCA\\NewsPlugin\\AppInfo; use OCP\\AppFramework\\App; use OCA\\News\\AppInfo\\Application as News; class Application extends App { public function __construct (array $urlParams=[]) { parent::__construct('newsplugin', $urlParams); $container = $this->getContainer(); $container->registerService('NewsContainer', function($c) { $app = new News(); return $app->getContainer(); }); $container->registerService(OCA\\News\\Service\\FeedService::class, function($c) { // use the feed service from the news app, you can use all // defined classes but its recommended that you stick to the // mapper and service classes since they are less likely to change return $c->query('NewsContainer')->query(OCA\\News\\Service\\FeedService::class); }); } } Using automatic container assembly you can then use it from your code by simply adding the type to your constructors.","title":"Server-Side Plugin"},{"location":"features/plugins/#examples","text":"Client-side plugins: Mail Share : Client-side plugin to share articles by email Server-side plugins: Feed Central : Publish your feeds as RSS","title":"Examples"},{"location":"features/themes/","text":"Themes Nextcloud News can look different with the following themes: Nextcloud News Themes","title":"Themes"},{"location":"features/themes/#themes","text":"Nextcloud News can look different with the following themes: Nextcloud News Themes","title":"Themes"}]} \ No newline at end of file