Code Monkey home page Code Monkey logo

dart-nextcloud's People

Contributors

duglah avatar fingeg avatar fkleon avatar provokateurin avatar vauvenal5 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

dart-nextcloud's Issues

Unhandled Exception: type 'Null' is not a subtype of type 'String' in type cast

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.

Feature Request: Creating Users

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?

Username vs Loginname

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.

Maintaining this library

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 :)

Search ?

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.

How to check if a client is connected?

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 :)

Error when sharing a directory publicly

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.

How to implement OAuth 2.0 with Flutter and NextCloud?

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

Dart Nextcloud Talk API not compatible with newest Talk API

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 broken

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

LateInitializationError: Field 'fileId' has not been initialized

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.

Add CI

Should run tests, check formatting and pana score.

Sub-Url bug in WebDavFile path

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.

WebDavFile with whitespace

Folders with whitespace or German Umlaute are not decoded.

Folder: mein ordner

will result in WebDavFile.path as:
mein%20ordner

nextcloud: ^3.0.1

If the file was copied to another folder, you can't get a preview of the image.

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?

Better path handeling in WebDAV and Previews

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.

mkdirs does not work as expected

Hello thank you for this package,

mkdirs-method for the WebDav-Module claims to work like mkdir -p.

Expected Behavior:
if I call mkdirs with path="/folder1/folder2/folder3" I should get:

Screenshot 2022-02-09 182400

Actual Behavior:

The method splits the directorynames up and creates them on the same directory-level.

Screenshot 2022-02-09 182533

Error parser Cliente->User->MetaData

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;
}
}
`

Documentation

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.

Wrong Content-Type in WebDAV requests

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>

Permissive license possible?

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

Some tests don't really work

I think especially the talk tests do nothing. They often just check that the response is not null which is insufficient for any test.

Update README.md

The features in the readme are somewhat outdated and a new readme in general would make sense I think

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.