nextcloud 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "nextcloud"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,47 @@
1
+ require "net/https"
2
+ require "nokogiri"
3
+
4
+ require "nextcloud/version/nextcloud"
5
+ require "nextcloud/errors/nextcloud"
6
+
7
+ require "nextcloud/helpers/nextcloud"
8
+ require "nextcloud/helpers/properties"
9
+
10
+ require "nextcloud/api"
11
+
12
+ require "nextcloud/ocs_api"
13
+ require "nextcloud/ocs/user"
14
+ require "nextcloud/ocs/group"
15
+ require "nextcloud/ocs/app"
16
+ require "nextcloud/ocs/file_sharing_api"
17
+
18
+ require "nextcloud/webdav_api"
19
+ require "nextcloud/webdav/directory"
20
+
21
+ require "nextcloud/models/user"
22
+ require "nextcloud/models/directory"
23
+
24
+ # Namespace for Nextcloud OCS API communication
25
+ module Nextcloud
26
+ class << self
27
+ # Access to OCS API from base instance
28
+ #
29
+ # @param [Hash] args authentication credentials.
30
+ # @option args [String] :url Nextcloud instance URL
31
+ # @option args [String] :username Nextcloud instance administrator username
32
+ # @option args [String] :password Nextcloud instance administrator password
33
+ def ocs(args)
34
+ OcsApi.new(args)
35
+ end
36
+
37
+ # Access to WebDAV API from base instance
38
+ #
39
+ # @param [Hash] args authentication credentials.
40
+ # @option args [String] :url Nextcloud instance URL
41
+ # @option args [String] :username Nextcloud instance administrator username
42
+ # @option args [String] :password Nextcloud instance administrator password
43
+ def webdav(args)
44
+ WebdavApi.new(args)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,51 @@
1
+ module Nextcloud
2
+ class Api
3
+ attr_reader :url, :username, :password
4
+ protected :url
5
+ protected :username
6
+ protected :password
7
+
8
+ # Gathers credentials for communicating with Nextcloud instance
9
+ #
10
+ # @param [Hash] args authentication credentials.
11
+ # @option args [String] :url Nextcloud instance URL
12
+ # @option args [String] :username Nextcloud instance administrator username
13
+ # @option args [String] :password Nextcloud instance administrator password
14
+ def initialize(args)
15
+ @url = URI(args[:url] + "/ocs/v2.php/cloud/")
16
+ @username = args[:username]
17
+ @password = args[:password]
18
+ end
19
+
20
+ # Sends API request to Nextcloud
21
+ #
22
+ # @param method [Symbol] Request type. Can be :get, :post, :put, etc.
23
+ # @param path [String] Nextcloud OCS API request path
24
+ # @param params [Hash, nil] Parameters to send
25
+ # @return [Object] Nokogiri::XML::Document
26
+ def request(method, path, params = nil, body = nil, depth = nil, destination = nil, raw = false)
27
+ response = Net::HTTP.start(@url.host, @url.port,
28
+ use_ssl: @url.scheme == "https") do |http|
29
+ req = Kernel.const_get("Net::HTTP::#{method.capitalize}").new(
30
+ @url.to_s + path, 'Content-Type': "application/x-www-form-urlencoded"
31
+ )
32
+ req["OCS-APIRequest"] = true
33
+ req.basic_auth @username, @password
34
+ req["Content-Type"] = "application/x-www-form-urlencoded"
35
+
36
+ req["Depth"] = 0 if depth
37
+ req["Destination"] = destination if destination
38
+
39
+ req.set_form_data(params) if params
40
+ req.body = body if body
41
+
42
+ http.request(req)
43
+ end
44
+
45
+ # if ![201, 204, 207].include? response.code
46
+ # raise Errors::Error.new("Nextcloud received invalid status code")
47
+ # end
48
+ raw ? response.body : Nokogiri::XML.parse(response.body)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,5 @@
1
+ module Nextcloud
2
+ module Errors
3
+ class Error < StandardError; end
4
+ end
5
+ end
@@ -0,0 +1,101 @@
1
+ require "active_support/core_ext/hash"
2
+ require "json"
3
+
4
+ module Nextcloud
5
+ # Helper methods that are used through lib
6
+ module Helpers
7
+ # Makes an array out of repeated elements
8
+ #
9
+ # @param doc [Object] Nokogiri::XML::Document
10
+ # @param xpath [String] Path to element that is being repeated
11
+ # @return [Array] Parsed array
12
+ def parse_with_meta(doc, xpath)
13
+ groups = []
14
+ doc.xpath(xpath).each do |prop|
15
+ groups << prop.text
16
+ end
17
+ meta = get_meta(doc)
18
+ groups.send(:define_singleton_method, :meta) do
19
+ meta
20
+ end
21
+ groups
22
+ end
23
+
24
+ # Parses meta information returned by API, may include a status, status code and a message
25
+ #
26
+ # @param doc [Object] Nokogiri::XML::Document
27
+ # @return [Hash] Parsed hash
28
+ def get_meta(doc)
29
+ meta = doc.xpath("//meta/*").each_with_object({}) do |node, meta|
30
+ meta[node.name] = node.text
31
+ end
32
+ end
33
+
34
+ # Converts document to hash
35
+ #
36
+ # @param doc [Object] Nokogiri::XML::Document
37
+ # @param xpath [String] Document path to convert to hash
38
+ # @return [Hash] Hash that was produced from XML document
39
+ def doc_to_hash(doc, xpath = "/")
40
+ h = Hash.from_xml(doc.xpath(xpath).to_xml)
41
+ h
42
+ end
43
+
44
+ # Adds meta method to an object
45
+ #
46
+ # @param doc [Object] Nokogiri::XML::Document to take meta information from
47
+ # @param obj [#define_singleton_method] Object to add meta method to
48
+ # @return [#define_singleton_method] Object with meta method defined
49
+ def add_meta(doc, obj)
50
+ meta = get_meta(doc)
51
+ obj.define_singleton_method(:meta) { meta } && obj
52
+ end
53
+
54
+ # Extracts remaining part of url
55
+ #
56
+ # @param href [String] Full url
57
+ # @param url [String] Part to give away
58
+ # @return [String] "Right" part of string
59
+ def path_from_href(href, url)
60
+ href.match(/#{url}(.*)/)[1]
61
+ end
62
+
63
+ # Shows errors, or success message
64
+ #
65
+ # @param doc [Object] Nokogiri::XML::Document
66
+ # @return [Hash] State response
67
+ def parse_dav_response(doc)
68
+ doc.remove_namespaces!
69
+ if doc.at_xpath("//error")
70
+ {
71
+ exception: doc.xpath("//exception").text,
72
+ message: doc.xpath("//message").text
73
+ }
74
+ elsif doc.at_xpath("//status")
75
+ {
76
+ status: doc.xpath("//status").text
77
+ }
78
+ else
79
+ {
80
+ status: "ok"
81
+ }
82
+ end
83
+ end
84
+
85
+ # Shows error or returns false
86
+ #
87
+ # @param doc [Object] Nokogiri::XML::Document
88
+ # @return [Hash,Boolean] Returns error message if found, false otherwise
89
+ def has_dav_errors(doc)
90
+ doc.remove_namespaces!
91
+ if doc.at_xpath("//error")
92
+ {
93
+ exception: doc.xpath("//exception").text,
94
+ message: doc.xpath("//message").text
95
+ }
96
+ else
97
+ false
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,73 @@
1
+ module Nextcloud
2
+ module Helpers
3
+ module Properties
4
+ # Body to send to receive item properties
5
+ RESOURCE = '<?xml version="1.0"?>
6
+ <d:propfind xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns" xmlns:nc="http://nextcloud.org/ns">
7
+ <d:prop>
8
+ <d:getlastmodified />
9
+ <d:getetag />
10
+ <d:resourcetype />
11
+ <d:getcontenttype />
12
+ <d:getcontentlength />
13
+ <oc:id />
14
+ <oc:fileid />
15
+ <oc:permissions />
16
+ <oc:size />
17
+ <nc:has-preview />
18
+ <oc:favorite />
19
+ <oc:comments-href />
20
+ <oc:comments-count />
21
+ <oc:comments-unread />
22
+ <oc:owner-id />
23
+ <oc:owner-display-name />
24
+ <oc:share-types />
25
+ <nc:has-preview />
26
+ </d:prop>
27
+ </d:propfind>'.freeze
28
+
29
+ # Body to send to add an item to favorites
30
+ MAKE_FAVORITE = '<?xml version="1.0"?>
31
+ <d:propertyupdate xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
32
+ <d:set>
33
+ <d:prop>
34
+ <oc:favorite>1</oc:favorite>
35
+ </d:prop>
36
+ </d:set>
37
+ </d:propertyupdate>'.freeze
38
+
39
+ # Body to send to unfavorite an item
40
+ UNFAVORITE = '<?xml version="1.0"?>
41
+ <d:propertyupdate xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns">
42
+ <d:set>
43
+ <d:prop>
44
+ <oc:favorite>0</oc:favorite>
45
+ </d:prop>
46
+ </d:set>
47
+ </d:propertyupdate>'.freeze
48
+
49
+ # Body to send for receiving favorites
50
+ FAVORITE = '<?xml version="1.0"?>
51
+ <oc:filter-files xmlns:d="DAV:" xmlns:oc="http://owncloud.org/ns" xmlns:nc="http://nextcloud.org/ns">
52
+ <oc:filter-rules>
53
+ <oc:favorite>1</oc:favorite>
54
+ </oc:filter-rules>
55
+ <d:prop>
56
+ <d:getlastmodified />
57
+ <d:getetag />
58
+ <d:getcontenttype />
59
+ <d:resourcetype />
60
+ <oc:fileid />
61
+ <oc:permissions />
62
+ <oc:size />
63
+ <d:getcontentlength />
64
+ <nc:has-preview />
65
+ <oc:favorite />
66
+ <oc:comments-unread />
67
+ <oc:owner-display-name />
68
+ <oc:share-types />
69
+ </d:prop>
70
+ </oc:filter-files>'.freeze
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,74 @@
1
+ module Nextcloud
2
+ module Models
3
+ # Directory model
4
+ #
5
+ # @!attribute [rw] href
6
+ # @return [String] File/directory location
7
+ # @!attribute [rw] lastmodified
8
+ # @return [String] Last modification time of file/directory
9
+ # @!attribute [rw] tag
10
+ # @return [Hash] Etag
11
+ # @!attribute [rw] resourcetype
12
+ # @return [String] Type of a resource
13
+ # @!attribute [rw] contenttype
14
+ # @return [String] Type of content
15
+ # @!attribute [rw] contentlength
16
+ # @return [String] Length of content
17
+ # @!attribute [rw] id
18
+ # @return [String] ID
19
+ # @!attribute [rw] fileid
20
+ # @return [String] Fileid
21
+ # @!attribute [rw] permissions
22
+ # @return [String] Permissions
23
+ # @!attribute [rw] has_preview
24
+ # @return [String] Has preview or not
25
+ # @!attribute [rw] favorite
26
+ # @return [String] Is favorited or not
27
+ # @!attribute [rw] comments_href
28
+ # @return [String] Address of comments
29
+ # @!attribute [rw] comments_count
30
+ # @return [String] Comments count
31
+ # @!attribute [rw] comments_unread
32
+ # @return [String] Unread comments
33
+ # @!attribute [rw] owner_id
34
+ # @return [String] Id of owner
35
+ # @!attribute [rw] owner_display_name
36
+ # @return [String] Display name of owner
37
+ # @!attribute [rw] share_types
38
+ # @return [String] Share types
39
+ class Directory
40
+ attr_accessor :meta, :contents
41
+
42
+ # Initiates a model instance
43
+ #
44
+ # @param [Hash]
45
+ def initialize(href: nil, lastmodified: nil, tag: nil, resourcetype: nil, contenttype: nil, contentlength: nil,
46
+ id: nil, fileid: nil, permissions: nil, size: nil, has_preview: nil, favorite: nil,
47
+ comments_href: nil, comments_count: nil, comments_unread: nil, owner_id: nil,
48
+ owner_display_name: nil, share_types: nil, skip_contents: false)
49
+
50
+ self.class.params.each do |v|
51
+ instance_variable_set("@#{v}", instance_eval(v.to_s)) if instance_eval(v.to_s)
52
+ end
53
+
54
+ remove_instance_variable (:@skip_contents) if skip_contents
55
+ end
56
+
57
+ @params = instance_method(:initialize).parameters.map(&:last)
58
+ @params.each { |p| instance_eval("attr_accessor :#{p}") }
59
+
60
+ class << self
61
+ attr_reader :params
62
+ end
63
+
64
+ # Adds content to collection
65
+ #
66
+ # @param [Hash]
67
+ # @return [Array] Contents array
68
+ def add(args)
69
+ @contents = [] if @contents.nil?
70
+ @contents << self.class.new(args.merge(skip_contents: true))
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,50 @@
1
+ module Nextcloud
2
+ module Models
3
+ # User model
4
+ #
5
+ # @!attribute [rw] enabled
6
+ # @return [String] Is an user enabled or not
7
+ # @!attribute [rw] id
8
+ # @return [String] Identifier of an user
9
+ # @!attribute [rw] quota
10
+ # @return [Hash] Quota of user
11
+ # @!attribute [rw] email
12
+ # @return [String] E-mail address
13
+ # @!attribute [rw] displayname
14
+ # @return [String] User display name
15
+ # @!attribute [rw] phone
16
+ # @return [String] User phone number
17
+ # @!attribute [rw] address
18
+ # @return [String] User address
19
+ # @!attribute [rw] website
20
+ # @return [String] User web-site address
21
+ # @!attribute [rw] twitter
22
+ # @return [String] User Twitter account
23
+ # @!attribute [rw] groups
24
+ # @return [String] Groups user belongs to
25
+ # @!attribute [rw] language
26
+ # @return [String] Nextcloud version for an user
27
+ class User
28
+ attr_accessor :meta
29
+
30
+ # Initiates a model instance
31
+ #
32
+ # @param [Hash]
33
+ def initialize(enabled: nil, id: nil, quota: nil, email: nil, displayname: nil, phone: nil, address: nil,
34
+ website: nil,
35
+ twitter: nil, groups: nil, language: nil)
36
+
37
+ self.class.params.each do |v|
38
+ instance_variable_set("@#{v}", instance_eval(v.to_s)) if instance_eval(v.to_s)
39
+ end
40
+ end
41
+
42
+ @params = instance_method(:initialize).parameters.map(&:last)
43
+ @params.each { |p| instance_eval("attr_accessor :#{p}") }
44
+
45
+ class << self
46
+ attr_reader :params
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,90 @@
1
+ module Nextcloud
2
+ module Ocs
3
+ # Application class used for interfering with app specific actions
4
+ #
5
+ # @!attribute [rw] meta
6
+ # @return [Hash] Information about API response
7
+ # @!attribute [rw] appid
8
+ # @return [Integer] Application identifier
9
+ class App < OcsApi
10
+ include Helpers
11
+
12
+ attr_accessor :meta, :appid
13
+
14
+ # Application initializer
15
+ #
16
+ # @param api [Object] Api instance
17
+ # @param appid [Integer,nil] Application identifier
18
+ def initialize(args, appid = nil)
19
+ @appid = appid if appid
20
+
21
+ if args.class == Nextcloud::OcsApi
22
+ @api = args
23
+ else
24
+ super(args)
25
+ @api = self
26
+ end
27
+ end
28
+
29
+ # Sets app (useful if class is initiated without OcsApi.app)
30
+ #
31
+ # @param userid [String] User identifier
32
+ # @return [Obeject] self
33
+ def set(appid)
34
+ @appid = appid
35
+ self
36
+ end
37
+
38
+ # List enabled applications
39
+ #
40
+ # @return [Array] List of applications that are enabled on an instance
41
+ def enabled
42
+ filter("enabled")
43
+ end
44
+
45
+ # List disabled applications
46
+ #
47
+ # @return [Array] List of applications that are disabled on an instance
48
+ def disabled
49
+ filter("disabled")
50
+ end
51
+
52
+ # Get information about an applicaiton
53
+ #
54
+ # @param appid [Integer] Application identifier
55
+ # @return [Hash] Application information
56
+ def find(appid)
57
+ response = @api.request(:get, "apps/#{appid}")
58
+ h = doc_to_hash(response, "//data")["data"]
59
+ add_meta(response, h)
60
+ end
61
+
62
+ # Enable an application
63
+ #
64
+ # @return [Object] Instance with meta response
65
+ def enable
66
+ response = @api.request(:post, "apps/#{@appid}")
67
+ (@meta = get_meta(response)) && self
68
+ end
69
+
70
+ # Disable an application
71
+ #
72
+ # @return [Object] Instance with meta response
73
+ def disable
74
+ response = @api.request(:delete, "apps/#{@appid}")
75
+ (@meta = get_meta(response)) && self
76
+ end
77
+
78
+ private
79
+
80
+ # Retrieve enabled or disabled applications
81
+ #
82
+ # @param filter [String] Either enabled or disabled
83
+ # @return [Array] List of applications with meta method
84
+ def filter(filter)
85
+ response = @api.request(:get, "apps?filter=#{filter}")
86
+ parse_with_meta(response, "//data/apps/element")
87
+ end
88
+ end
89
+ end
90
+ end