provokateurin / dart-nextcloud Goto Github PK
View Code? Open in Web Editor NEWA Nextcloud client for dart
License: Other
A Nextcloud client for dart
License: Other
Hi,
Tried this simple example
Future createFolder() async { try { final client = NextCloudClient.withCredentials( Uri(host: '---'), 'xxx', 'xxx', ); final files = await client.webDav.ls('/'); for (final file in files){ print(file.path); } } on RequestException catch (e, stacktrace) { print(e.statusCode); print(e.body); print(stacktrace); } }
but get error:
Unhandled Exception: type 'Null' is not a subtype of type 'String' in type cast
E/flutter (28814): #0 new UserData.fromJson (package:nextcloud/src/user/userdata.dart:15:38)
E/flutter (28814): #1 UserClient.getUser (package:nextcloud/src/user/client.dart:21:21)
E/flutter (28814): ......
I did everything correct right?
Thanks for help.
Hi, when creating an instance of NextCloudClient
I can log in as user.
However, I would also like to create users with this Dart Plugin.
How would that be possible?
I am getting this error while I try to use getshares(). please can some help me.
As the Nextcloud login flow docs mention there might be cases where the login name is not the same as the username needed for the Webdav URL. Therefor it is necessary to provide access to the <server>/ocs/v1.php/cloud/user
OCS API endpoint to be able to fetch the real username.
This process could be enclosed in the library itself in form of a login method or made available just as a simple call like everything else.
I haven't worked on this library much in the last months and neither did @fingeg.
At least my reason for not working on this library is, that I currently have no app that makes use of Nextcloud somewhere. This might change in the future though, because I'm moving stuff to a personal Nextcloud instance and maybe there is also some place to code an app for a specific task.
I don't want this library to die, so it would be cool if people would contribute to fix stuff and add new features.
I'm willing to give access to the repository to trustworthy people.
@vauvenal5 I think you could be one of these persons, as you have been working with this library in your app and you also contributed some good work already. You don't need to, but feel free to work on it if you want :)
Thanks for informations,
Can you help me with a subject I'm stuck on?
I want to search within the folder. How can I do it?
Also how can I view pdf.
Nice work! Glad to have this plugin.
It would be nice if the client would provide a possibility to check if it was initialised with valid credentials. Any ideas how to do this?
Thanks for your great work again :)
There are two occurrences of remote.php/webdav
in the code where I strongly suspect that it is a typo and should be remote.php/dav
. Am I missing something?
When I try to share a directory I get the following error:
Unhandled Exception: XmlParserException: "<" expected at 1:1
#0 new XmlDocument.parse (package:xml/src/xml/nodes/document.dart:35:7)
#1 sharesFromSharesXml (package:nextcloud/src/shares/share.dart:227:39)
#2 SharesClient.getShares (package:nextcloud/src/shares/client.dart:43:12)
My code:
// example of folderName: '/NewFolder'
Future<void> createShare(String folderNameToShare) {
var ncClient = NextCloudClient.withCredentials(Uri(host: baseUrl), "[email protected]", "myPassword");
await ncClient.webDav.mkdir(folderNameToShare);
await ncClient.shares.shareWithPublicLink(folderNameToShare, permissions: Permissions([
Permission.read, Permission.update
]));
}
The share url gets created on Nextcloud but the error as mentioned above occurs.
What I also noticed is that even though I used permissions: Permissions([Permission.read, Permission.update])
for the shareWithPublicLink
function, the folder in the end only has read permissions on Nextcloud.
The same error as mentioned above occurs when I try to get the share link:
List<Share> shares = await ncClient.shares.getShares(path: folderNameToShare);
if (shares.length>0) {
print(shares.first.url);
}
Any help again will be very welcome.
I'm new to Flutter/Dart.
I'm struggling a lot to be able to use an authentication other than the basic hardcoded user/password, which I understand is not a good practice, but is the only example shown in the plugin.
Does anyone have been able to implement this and could share it pleeease?
All the examples I found were related to user authentication, which is not what I want, I want to access NextCloud services in the background, not something the user will see. Other examples had limited implementations and the examples talked a lot about having to open the browser for a redirect to happen? This is not what I would expect.
Anyway, again, I'm new to this. Let me know your thoughts for a best practice approach.
Thank you in advance!
Will
client.dart for talk API specifies the following baseUrl for talk management:
final _baseUrl = '$baseUrl/ocs/v2.php/apps/spreed/api/v1';
However, though this may be true for _guestManagement, _messageManagement it does not apply to
_conversationManagement anymore.
The correct baseUrl would be:
final _baseUrl4 = '$baseUrl/ocs/v2.php/apps/spreed/api/v4';
_conversationManagement = ConversationManagement(network, _baseUrl4);
Furthermore 2 members marked as required in Conversation have been removed, therefore they should be removed in order for the talk API to be intact again for new Nextcloud Talk version.
The 2 members affected, that should be removed, are:
/// The count of active users
final int userCount;
/// The number of active guests
final int guestCount;
I tried this on my local machine and the Talk API basic functions work again. Hope it helps in keeping future compatibility.
Talk getting participants test is broken in some cases, because it returns some id which looks randomly generated and is not the user id. It's actually the same id used for the file path in my scenario
Login flow V2 is browser based but also needs a preparation call and a polling call to finish the login flow. I would contribute those two if you do not mind having them in the library. Since they are purely between target client and NC server I believe being in the library is okay. NC docs: https://docs.nextcloud.com/server/latest/developer_manual/client_apis/LoginFlow/index.html#login-flow-v2
I want to be able to get the fileId
of a WebDavFile
.
I first loaded the files:
//...
var ncClient = NextCloudClient.withCredentials(Uri(host: baseUrl), "[email protected]", "myPassword");
//...
List<WebDavFile> allFiles = await ncClient.webDav.ls(path, props: WebDavProps.all);
Afterwards when I want to access the fileId
:
print(allFiles[index].fileId); //throws LateInit error
var res = await ncClient.webDav.getProps("/Documents/file.png", props: WebDavProps.all);
print(res.fileId); //throws LateInit error
And I get the following error: LateInitializationError: Field 'fileId' has not been initialized
Under the Nextcloud WebDAV API for Requesting Properties I saw that the fileId
was defined as <oc:fileid />
.
So I also tried to get the Property manually with getOtherProp()
but I'm not quite sure if I used it correctly:
print(allFiles[idx].getOtherProp('fileid', "http://owncloud.org/ns")); //returns null
print(allFiles[idx].getOtherProp('oc:fileid', "http://owncloud.org/ns")); //returns null
print(allFiles[idx].getOtherProp('fileid', "oc")); //returns null
I also looked at other properties. The following properties apart from fileId
also threw a LateInit error:
note, createdDate, uploadedDate
Any help or fixing of the LateInit error would be appreciated.
How is it possible to have something similar to Firebase Cloud Messaging, where when a user receives a chat message on Talk while the app is closed, the user then receives a push notification?
Should run tests, check formatting and pana score.
There is an issue when the Nextcloud server is behind a sub-path, for example https://cloud.com/nc
. When parsing the XML returned by the server replaceAll
replaces the davUrl
in the file path but leaves the nc
part of the server path in there. A split operation would probably be a better option to make sure that the parts belonging to the server address are cut away.
Sorry when reviewing the file path handling PR I did not think of it, just came to mind now when I was about to integrate the newest version of the library.
If a folder is named like :443
and placed in the directory root of the Nextcloud server then a FormatException
gets thrown when webDavFile.name
is called because Uri.parse
fails to recognize it as a name.
Folders with whitespace or German Umlaute are not decoded.
Folder: mein ordner
will result in WebDavFile.path as:
mein%20ordner
nextcloud: ^3.0.1
Hello, I found this problem:
If I copied the image to another folder, I can't get the preview via remotePath. As I understand it, nextcloud preview images are taken through the ID, not through the path, but you do not have the implementation of getting the ID.
How can I fix this myself in my code, or you need to fix it on your side?
The handling of paths between WebDAV and previews is different. WebDAV expects you to pass paths like /files/username/test.png
while previews expects you to pass paths like test.png
.
The previews approach should be preferred.
As far as I saw currently the activity log endpoint is not supported: https://github.com/nextcloud/activity/blob/master/docs/endpoint-v2.md
I am opening this here also to have the docs for the API at the right place, however, at the moment I do not have time to contribute this feature. But, if no one else tackles this first, I will get back to it, eventually. ๐
When trying to consume the method client.users.getMetaData(userId);
Return the following error:
Expected a value of type 'List', but got one of type 'List'
The error occurs in the MetaData class
Future<MetaData> getMetaData(String username) async { final url = '$_baseUrl/$username'; final response = await _network.send('GET', url, [200]); return MetaData.fromJson( jsonDecode(response.body) as Map<String, dynamic>, ); }
Code
`/// MetaData class
class MetaData {
// ignore: public_member_api_docs
MetaData(
this.fullName,
this.groups,
);
// ignore: public_member_api_docs
factory MetaData.fromJson(Map<String, dynamic> json) => MetaData(
json['ocs']['data']['displayname'] as String,
json['ocs']['data']['groups'] as List,
);
// ignore: public_member_api_docs
final String fullName;
// ignore: public_member_api_docs
final List groups;
@OverRide
String toString() => 'MetaData{fullName: $fullName ,groups: $groups}}';
}
`
Therefore, add the MetaDataUser class, which does not return an error.
Future<MetaDataUser > getMetaData(String username) async { final url = '$_baseUrl/$username'; final response = await _network.send('GET', url, [200]); return MetaDataUser .fromJson( jsonDecode(response.body) as Map<String, dynamic>, ); }
`
class MetaDataUser {
Ocs? _ocs;
MetaDataUser({Ocs? ocs}) {
if (ocs != null) {
this._ocs = ocs;
}
}
Ocs? get ocs => _ocs;
set ocs(Ocs? ocs) => _ocs = ocs;
MetaDataUser.fromJson(Map<String, dynamic> json) {
_ocs = json['ocs'] != null ? new Ocs.fromJson(json['ocs']) : null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this._ocs != null) {
data['ocs'] = this._ocs!.toJson();
}
return data;
}
}
class Ocs {
Meta? _meta;
Data? _data;
Ocs({Meta? meta, Data? data}) {
if (meta != null) {
this._meta = meta;
}
if (data != null) {
this._data = data;
}
}
Meta? get meta => _meta;
set meta(Meta? meta) => _meta = meta;
Data? get data => _data;
set data(Data? data) => _data = data;
Ocs.fromJson(Map<String, dynamic> json) {
_meta = json['meta'] != null ? new Meta.fromJson(json['meta']) : null;
_data = json['data'] != null ? new Data.fromJson(json['data']) : null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
if (this._meta != null) {
data['meta'] = this._meta!.toJson();
}
if (this._data != null) {
data['data'] = this._data!.toJson();
}
return data;
}
}
class Meta {
String? _status;
int? _statuscode;
String? _message;
String? _totalitems;
String? _itemsperpage;
Meta(
{String? status,
int? statuscode,
String? message,
String? totalitems,
String? itemsperpage}) {
if (status != null) {
this._status = status;
}
if (statuscode != null) {
this._statuscode = statuscode;
}
if (message != null) {
this._message = message;
}
if (totalitems != null) {
this._totalitems = totalitems;
}
if (itemsperpage != null) {
this._itemsperpage = itemsperpage;
}
}
String? get status => _status;
set status(String? status) => _status = status;
int? get statuscode => _statuscode;
set statuscode(int? statuscode) => _statuscode = statuscode;
String? get message => _message;
set message(String? message) => _message = message;
String? get totalitems => _totalitems;
set totalitems(String? totalitems) => _totalitems = totalitems;
String? get itemsperpage => _itemsperpage;
set itemsperpage(String? itemsperpage) => _itemsperpage = itemsperpage;
Meta.fromJson(Map<String, dynamic> json) {
_status = json['status'];
_statuscode = json['statuscode'];
_message = json['message'];
_totalitems = json['totalitems'];
_itemsperpage = json['itemsperpage'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['status'] = this._status;
data['statuscode'] = this._statuscode;
data['message'] = this._message;
data['totalitems'] = this._totalitems;
data['itemsperpage'] = this._itemsperpage;
return data;
}
}
class Data {
bool? _enabled;
String? _storageLocation;
String? _id;
int? _lastLogin;
String? _backend;
List? _subadmin;
Quota? _quota;
Null? _email;
String? _displayname;
String? _phone;
String? _address;
String? _website;
String? _twitter;
List? _groups;
String? _language;
String? _locale;
BackendCapabilities? _backendCapabilities;
Data(
{bool? enabled,
String? storageLocation,
String? id,
int? lastLogin,
String? backend,
List? subadmin,
Quota? quota,
Null? email,
String? displayname,
String? phone,
String? address,
String? website,
String? twitter,
List? groups,
String? language,
String? locale,
BackendCapabilities? backendCapabilities}) {
if (enabled != null) {
this._enabled = enabled;
}
if (storageLocation != null) {
this._storageLocation = storageLocation;
}
if (id != null) {
this._id = id;
}
if (lastLogin != null) {
this._lastLogin = lastLogin;
}
if (backend != null) {
this._backend = backend;
}
if (subadmin != null) {
this._subadmin = subadmin;
}
if (quota != null) {
this._quota = quota;
}
if (email != null) {
this._email = email;
}
if (displayname != null) {
this._displayname = displayname;
}
if (phone != null) {
this._phone = phone;
}
if (address != null) {
this._address = address;
}
if (website != null) {
this._website = website;
}
if (twitter != null) {
this._twitter = twitter;
}
if (groups != null) {
this._groups = groups;
}
if (language != null) {
this._language = language;
}
if (locale != null) {
this._locale = locale;
}
if (backendCapabilities != null) {
this._backendCapabilities = backendCapabilities;
}
}
bool? get enabled => _enabled;
set enabled(bool? enabled) => _enabled = enabled;
String? get storageLocation => _storageLocation;
set storageLocation(String? storageLocation) =>
_storageLocation = storageLocation;
String? get id => _id;
set id(String? id) => _id = id;
int? get lastLogin => _lastLogin;
set lastLogin(int? lastLogin) => _lastLogin = lastLogin;
String? get backend => _backend;
set backend(String? backend) => _backend = backend;
List? get subadmin => _subadmin;
set subadmin(List? subadmin) => _subadmin = subadmin;
Quota? get quota => _quota;
set quota(Quota? quota) => _quota = quota;
Null? get email => _email;
set email(Null? email) => _email = email;
String? get displayname => _displayname;
set displayname(String? displayname) => _displayname = displayname;
String? get phone => _phone;
set phone(String? phone) => _phone = phone;
String? get address => _address;
set address(String? address) => _address = address;
String? get website => _website;
set website(String? website) => _website = website;
String? get twitter => _twitter;
set twitter(String? twitter) => _twitter = twitter;
List? get groups => _groups;
set groups(List? groups) => _groups = groups;
String? get language => _language;
set language(String? language) => _language = language;
String? get locale => _locale;
set locale(String? locale) => _locale = locale;
BackendCapabilities? get backendCapabilities => _backendCapabilities;
set backendCapabilities(BackendCapabilities? backendCapabilities) =>
_backendCapabilities = backendCapabilities;
Data.fromJson(Map<String, dynamic> json) {
_enabled = json['enabled'];
_storageLocation = json['storageLocation'];
_id = json['id'];
_lastLogin = json['lastLogin'];
_backend = json['backend'];
_subadmin = json['subadmin'].cast();
_quota = json['quota'] != null ? new Quota.fromJson(json['quota']) : null;
_email = json['email'];
_displayname = json['displayname'];
_phone = json['phone'];
_address = json['address'];
_website = json['website'];
_twitter = json['twitter'];
_groups = json['groups'].cast();
_language = json['language'];
_locale = json['locale'];
_backendCapabilities = json['backendCapabilities'] != null
? new BackendCapabilities.fromJson(json['backendCapabilities'])
: null;
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['enabled'] = this._enabled;
data['storageLocation'] = this._storageLocation;
data['id'] = this._id;
data['lastLogin'] = this._lastLogin;
data['backend'] = this._backend;
data['subadmin'] = this._subadmin;
if (this._quota != null) {
data['quota'] = this._quota!.toJson();
}
data['email'] = this._email;
data['displayname'] = this._displayname;
data['phone'] = this._phone;
data['address'] = this._address;
data['website'] = this._website;
data['twitter'] = this._twitter;
data['groups'] = this._groups;
data['language'] = this._language;
data['locale'] = this._locale;
if (this._backendCapabilities != null) {
data['backendCapabilities'] = this._backendCapabilities!.toJson();
}
return data;
}
}
class Quota {
int? _free;
int? _used;
int? _total;
int? _relative;
int? _quota;
Quota({int? free, int? used, int? total, int? relative, int? quota}) {
if (free != null) {
this._free = free;
}
if (used != null) {
this._used = used;
}
if (total != null) {
this._total = total;
}
if (relative != null) {
this._relative = relative;
}
if (quota != null) {
this._quota = quota;
}
}
int? get free => _free;
set free(int? free) => _free = free;
int? get used => _used;
set used(int? used) => _used = used;
int? get total => _total;
set total(int? total) => _total = total;
int? get relative => _relative;
set relative(int? relative) => _relative = relative;
int? get quota => _quota;
set quota(int? quota) => _quota = quota;
Quota.fromJson(Map<String, dynamic> json) {
_free = json['free'];
_used = json['used'];
_total = json['total'];
_relative = json['relative'];
_quota = json['quota'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['free'] = this._free;
data['used'] = this._used;
data['total'] = this._total;
data['relative'] = this._relative;
data['quota'] = this._quota;
return data;
}
}
class BackendCapabilities {
bool? _setDisplayName;
bool? _setPassword;
BackendCapabilities({bool? setDisplayName, bool? setPassword}) {
if (setDisplayName != null) {
this._setDisplayName = setDisplayName;
}
if (setPassword != null) {
this._setPassword = setPassword;
}
}
bool? get setDisplayName => _setDisplayName;
set setDisplayName(bool? setDisplayName) => _setDisplayName = setDisplayName;
bool? get setPassword => _setPassword;
set setPassword(bool? setPassword) => _setPassword = setPassword;
BackendCapabilities.fromJson(Map<String, dynamic> json) {
_setDisplayName = json['setDisplayName'];
_setPassword = json['setPassword'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['setDisplayName'] = this._setDisplayName;
data['setPassword'] = this._setPassword;
return data;
}
}
`
It might be nice to have some basic documentation for this plugin.
The README.md
lists the features of this plugin, but doesn't contain a list of supported functions, for example.
The example.dart
contains examples of different functions, but examples for Talk are missing, which makes it harder to use this plugin. This inevitably forces you to look closer into the source code of this plugin.
Even though this plugin is already pretty nice, I might add.
Hi, I have mod-security enabled in my Nextcloud installation. It is blocking WebDAV requests because the content-type is wrong (it should be text/xml):
PROPFIND /remote.php/dav//files/XXXXX/ HTTP/1.1
user-agent: Nextcloud Yaga
accept: application/json
accept-encoding: gzip
content-length: 361
host: XXXXXXXX
authorization: Basic XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
content-type: application/json
ocs-apirequest: true
x-forwarded-port: 443
x-proxy-token: XXXXXXXXXXXXXXXXXXXXXXXXXXXX
connection: close
<?xml version="1.0"?><d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns" xmlns:nc="http://nextcloud.org/ns" xmlns:ocs="http://open-collaboration-services.org/ns" xmlns:ocm="http://open-cloud-mesh.org/ns" xmlns:s="http://sabredav.org/ns"><d:prop><d:getcontentlength/><d:getcontenttype/><d:getlastmodified/><oc:id/><oc:share-types/></d:prop></d:propfind>
Hello,
Is there a possibility to override the inner httpClient to ignore self signer certificates?
Implementation already living in https://github.com/jld3103/dart-nextcloud/tree/feature/notifications, but it is based on a very old version of this repository and and a lot of things have changed since then. Also the login flow already has been rebased and merged a while ago.
Hi @jld3103, I would like to use this library in a commercially used closed-source Flutter app for mobile platforms as an interface to NextCloud. But since it is licensed under the GPLv3 it is (at least based on my understanding) not possible for me to use it in an app that is released to the Google Play Store or the Apple App Store. Would it be possible for you to change the license to a more permissive one like MIT or BSD or provide the library with a dual license?
Thanks for your help and also for your work so far on this great project.
Kind regards
I think especially the talk tests do nothing. They often just check that the response is not null which is insufficient for any test.
The features in the readme are somewhat outdated and a new readme in general would make sense I think
The user-agent is used for the login flow to display to the user what client is requesting access, therefore, it should be possible to pass it from outside.
The WebDavClient
appends remote.php/dav/
twice to the provided url. Once in the constructor and once in _getUrl
function. This leads to a wrong url.
https://github.com/Viktoriaschule/dart-nextcloud/blob/f99a79b3590ac572a3d5952a3cba149840d6772e/lib/src/webdav/client.dart#L17
https://github.com/Viktoriaschule/dart-nextcloud/blob/f99a79b3590ac572a3d5952a3cba149840d6772e/lib/src/webdav/client.dart#L35
Hi,
I am using this library to create a Nextcloud first Photos App for Android. What I am missing is access to image previews. If it is fine with you I will contribute the necessary implementation.
I intend to provide access to the same two endpoints that are used in the Java library as mentioned here:
https://help.nextcloud.com/t/getting-image-preview-with-android-library-or-via-webdav/75743
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.