From e4d21f53afa8b222bd504a1d5c1470f5e77f2cf6 Mon Sep 17 00:00:00 2001 From: Hong Ooi Date: Wed, 23 Dec 2020 01:22:47 +1100 Subject: [PATCH] initial commit --- .Rbuildignore | 10 ++ .gitignore | 39 +++++++ DESCRIPTION | 29 +++++ LICENSE | 2 + LICENSE.md | 21 ++++ NAMESPACE | 7 ++ R/SharepointR.R | 25 +++++ R/add_methods.R | 62 ++++++++++ R/ms_drive.R | 250 +++++++++++++++++++++++++++++++++++++++++ R/ms_drive_item.R | 69 ++++++++++++ R/ms_sharepoint_list.R | 54 +++++++++ R/ms_site.R | 102 +++++++++++++++++ man/ms_drive.Rd | 102 +++++++++++++++++ man/ms_drive_item.Rd | 63 +++++++++++ man/ms_site.Rd | 59 ++++++++++ 15 files changed, 894 insertions(+) create mode 100644 .Rbuildignore create mode 100644 .gitignore create mode 100644 DESCRIPTION create mode 100644 LICENSE create mode 100644 LICENSE.md create mode 100644 NAMESPACE create mode 100644 R/SharepointR.R create mode 100644 R/add_methods.R create mode 100644 R/ms_drive.R create mode 100644 R/ms_drive_item.R create mode 100644 R/ms_sharepoint_list.R create mode 100644 R/ms_site.R create mode 100644 man/ms_drive.Rd create mode 100644 man/ms_drive_item.Rd create mode 100644 man/ms_site.Rd diff --git a/.Rbuildignore b/.Rbuildignore new file mode 100644 index 0000000..0f1083b --- /dev/null +++ b/.Rbuildignore @@ -0,0 +1,10 @@ +^misc$ +^\.vs$ +\.sln$ +\.Rproj$ +\.Rxproj$ +^\.Rproj\.user$ +CONTRIBUTING.md +^LICENSE\.md$ +^SECURITY\.md$ +azure-pipelines.yml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fae8299 --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +# History files +.Rhistory +.Rapp.history + +# Session Data files +.RData + +# User-specific files +.Ruserdata + +# Example code in package build process +*-Ex.R + +# Output files from R CMD build +/*.tar.gz + +# Output files from R CMD check +/*.Rcheck/ + +# RStudio files +.Rproj.user/ + +# produced vignettes +vignettes/*.html +vignettes/*.pdf + +# OAuth2 token, see https://github.com/hadley/httr/releases/tag/v0.3 +.httr-oauth + +# knitr and R markdown default cache directories +*_cache/ +/cache/ + +# Temporary files created by R markdown +*.utf8.md +*.knit.md + +# R Environment Variables +.Renviron diff --git a/DESCRIPTION b/DESCRIPTION new file mode 100644 index 0000000..ac38d7e --- /dev/null +++ b/DESCRIPTION @@ -0,0 +1,29 @@ +Package: SharePointR +Title: Simple Interface to 'SharePoint 365' and 'OneDrive' +Version: 0.0.1 +Authors@R: c( + person("Hong", "Ooi", , "hongooi73@gmail.com", role = c("aut", "cre")) + ) +Description: An interface to Microsoft 'SharePoint 365' and 'OneDrive'. +URL: https://github.com/hongooi73/SharePointR https://github.com/AzureRSDK/AzureR +BugReports: https://github.com/hongooi73/SharePointR/issues +License: MIT + file LICENSE +VignetteBuilder: knitr +Depends: + R (>= 3.3) +Imports: + AzureGraph (>= 1.1.2.9000), + utils, + httr, + jsonlite, + openssl, + R6, + vctrs +Suggests: + knitr, + rmarkdown, + testthat +Roxygen: list(markdown=TRUE, r6=FALSE, old_usage=TRUE) +RoxygenNote: 7.1.1 +Remotes: + hongooi73/AzureGraph diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4fac04c --- /dev/null +++ b/LICENSE @@ -0,0 +1,2 @@ +YEAR: 2020 +COPYRIGHT HOLDER: Hong Ooi diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..fff1674 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ +# MIT License + +Copyright (c) 2020 Hong Ooi. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE diff --git a/NAMESPACE b/NAMESPACE new file mode 100644 index 0000000..8d20f41 --- /dev/null +++ b/NAMESPACE @@ -0,0 +1,7 @@ +# Generated by roxygen2: do not edit by hand + +export(ms_drive) +export(ms_drive_item) +export(ms_sharepoint_list) +export(ms_site) +import(AzureGraph) diff --git a/R/SharepointR.R b/R/SharepointR.R new file mode 100644 index 0000000..3e904fd --- /dev/null +++ b/R/SharepointR.R @@ -0,0 +1,25 @@ +#' @import AzureGraph + +.onLoad <- function(libname, pkgname) +{ + register_graph_class("site", ms_site, + function(props) grepl("sharepoint", props$id, fixed=TRUE)) + + register_graph_class("drive", ms_drive, + function(props) !is_empty(props$driveType) && is_empty(props$parentReference)) + + register_graph_class("driveItem", ms_drive_item, + function(props) !is_empty(props$parentReference$driveId)) + + register_graph_class("list", ms_sharepoint_list, + function(props) !is_empty(props$list)) + + add_methods() +} + +# default authentication app ID: leverage the az CLI +.az_cli_app_id <- "04b07795-8ddb-461a-bbee-02f9e1bf7b46" + +# authentication app ID for personal accounts +.azurer_graph_app_id <- "5bb21e8a-06bf-4ac4-b613-110ac0e582c1" + diff --git a/R/add_methods.R b/R/add_methods.R new file mode 100644 index 0000000..4bfb73f --- /dev/null +++ b/R/add_methods.R @@ -0,0 +1,62 @@ +add_methods <- function() +{ + ms_graph$set("public", "get_sharepoint_site", overwrite=TRUE, + function(site_url=NULL, site_id=NULL) + { + op <- if(is.null(site_url) && !is.null(site_id)) + file.path("sites", site_id) + else if(!is.null(site_url) && is.null(site_id)) + { + site_url <- httr::parse_url(site_url) + file.path("sites", paste0(site_url$hostname, ":"), site_url$path) + } + else stop("Must supply either site ID or URL") + + ms_site$new(self$token, self$tenant, self$call_graph_endpoint(op)) + }) + + ms_graph$set("public", "get_drive", overwrite=TRUE, + function(drive_id) + { + op <- file.path("drives", drive_id) + ms_drive$new(self$token, self$tenant, self$call_graph_endpoint(op)) + }) + + az_user$set("public", "list_drives", overwrite=TRUE, + function() + { + res <- private$get_paged_list(self$do_operation("drives")) + private$init_list_objects(res, "drive") + }) + + az_user$set("public", "get_drive", overwrite=TRUE, + function(drive_id=NULL) + { + op <- if(is.null(drive_id)) + "drive" + else file.path("drives", drive_id) + ms_drive$new(self$token, self$tenant, self$do_operation(op)) + }) + + az_group$set("public", "get_sharepoint_site", overwrite=TRUE, + function() + { + res <- self$do_operation("sites/root") + ms_site$new(self$token, self$tenant, res) + }) + + az_group$set("public", "list_drives", overwrite=TRUE, + function() + { + res <- private$get_paged_list(self$do_operation("drives")) + private$init_list_objects(res, "drive") + }) + + az_group$set("public", "get_drive", overwrite=TRUE, function(drive_id=NULL) + { + op <- if(is.null(drive_id)) + "drive" + else file.path("drives", drive_id) + ms_drive$new(self$token, self$tenant, self$do_operation(op)) + }) +} diff --git a/R/ms_drive.R b/R/ms_drive.R new file mode 100644 index 0000000..786f22c --- /dev/null +++ b/R/ms_drive.R @@ -0,0 +1,250 @@ +#' Personal OneDrive or SharePoint document library +#' +#' Class representing a personal OneDrive or SharePoint document library. +#' +#' @docType class +#' @section Fields: +#' - `token`: The token used to authenticate with the Graph host. +#' - `tenant`: The Azure Active Directory tenant for this drive. +#' - `type`: always "drive" for a drive object. +#' - `properties`: The drive properties. +#' @section Methods: +#' - `new(...)`: Initialize a new drive object. Do not call this directly; see 'Initialization' below. +#' - `delete(confirm=TRUE)`: Delete a drive. By default, ask for confirmation first. +#' - `update(...)`: Update the drive metadata in Microsoft Graph. +#' - `do_operation(...)`: Carry out an arbitrary operation on the drive. +#' - `sync_fields()`: Synchronise the R object with the drive metadata in Microsoft Graph. +#' - `list_items(path, info, full.names)`: List the files and folders under the specified path. See 'File and folder operations' below. +#' - `download_file(src, dest, overwrite)`: Download a file. +#' - `upload_file(src, dest, blocksize)`: Upload a file. +#' - `create_folder(path)`: Create a folder. +#' - `delete_item(path, confirm)`: Delete a file or folder. +#' - `get_item_properties(path)`: Get the properties (metadata) for a file or folder. +#' - `set_item_properties(path, ...)`: Set the properties for a file or folder. +#' +#' @section Initialization: +#' Creating new objects of this class should be done via the `get_drive` methods of the [ms_graph], [az_user] or [ms_site] classes. Calling the `new()` method for this class only constructs the R object; it does not call the Microsoft Graph API to retrieve or create the actual drive. +#' +#' @section File and folder operations: +#' This class exposes methods for carrying out common operations on files and folders. +#' +#' `list_items` lists the items under the specified path. It is the analogue of base R's `dir`/`list.files`. The arguments are +#' - `path`: The path. +#' - `info`: The information to return: either "partial", "name" or "all". If "partial", a data frame is returned containing the name, size and whether the item is a file or folder. If "name", a vector of file/folder names is returned. If "all", a data frame is returned containing _all_ the properties for each item (this can be large). +#' - `full.names`: Whether to prefix the full path to the names of the items. +#' +#' `download_file` and `upload_file` download and upload files from the local machine to the drive. For `upload_file`, the uploading is done in blocks of 32MB by default; you can change this by setting the `blocksize` argument. For technical reasons, the block size [must be a multiple of 320KB](https://docs.microsoft.com/en-us/graph/api/driveitem-createuploadsession?view=graph-rest-1.0#upload-bytes-to-the-upload-session). +#' +#' `create_folder` creates a folder with the specified path. Trying to create an already existing folder is an error. +#' +#' `delete_item` deletes a file or folder. By default, it will ask for confirmation first. +#' +#' `get_item_properties` returns an object of [ms_drive_item], containing the properties (metadata) for a given file or folder. The properties can be found in the `properties` field of this object. +#' +#' `set_item_properties` sets the properties (metadata) of a file or folder. The new properties should be specified as individual named arguments to the method. Any existing properties that aren't listed as arguments will retain their previous values or be recalculated based on changes to other properties, as appropriate. +#' +#' @seealso +#' [ms_graph], [ms_site], [ms_drive_item] +#' +#' [Microsoft Graph overview](https://docs.microsoft.com/en-us/graph/overview), +#' [REST API reference](https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0) +#' +#' @examples +#' \dontrun{ +#' +#' gr <- get_graph_login() +#' +#' # shared document library for a SharePoint site +#' site <- gr$get_sharepoint_site("https://contoso.sharepoint.com/sites/O365-UG-123456") +#' drv <- site$get_drive() +#' +#' # personal OneDrive +#' gr2 <- get_graph_login("consumers") +#' me <- gr2$get_user() +#' mydrv <- me$get_drive() +#' +#' ## file/folder operationss +#' drv$list_items() +#' drv$list_items("path/to/folder", full.names=TRUE) +#' +#' # download a file -- default destination filename is taken from the source +#' drv$download_file("path/to/folder/data.csv") +#' +#' myfile <- drv$get_item_properties("myfile") +#' myfile$properties +#' +#' # rename a file +#' drv$set_item_properties("myfile", name="newname") +#' +#' } +#' @format An R6 object of class `ms_drive`, inheriting from `ms_object`. +#' @export +ms_drive <- R6::R6Class("ms_drive", inherit=ms_object, + +public=list( + + initialize=function(token, tenant=NULL, properties=NULL) + { + self$type <- "drive" + private$api_type <- "drives" + super$initialize(token, tenant, properties) + }, + + list_items=function(path="/", info=c("partial", "name", "all"), full.names=FALSE, pagesize=1000) + { + info <- match.arg(info) + opts <- switch(info, + partial=list(`$select`="name,size,folder", `$top`=pagesize), + name=list(`$select`="name", `$top`=pagesize), + list(`$top`=pagesize) + ) + + children <- if(path != "/") + { + # get the item corresponding to this path, then list its children + if(substr(path, 1, 1) != "/") + path <- paste0("/", path) + op <- file.path("root:", utils::URLencode(path, reserved=TRUE)) + item <- self$do_operation(op) + self$do_operation(file.path("items", item$id, "children"), options=opts, simplify=TRUE) + } + else self$do_operation("root/children", options=opts, simplify=TRUE) + + # get file list as a data frame + df <- private$get_paged_list(children, simplify=TRUE) + + if(is_empty(df)) + df <- data.frame(name=character(), size=numeric(), isdir=logical()) + else if(info != "name") + { + df$isdir <- if(!is.null(df$folder)) + !is.na(df$folder$childCount) + else rep(FALSE, nrow(df)) + } + + if(full.names) + df$name <- file.path(sub("^/", "", path), df$name) + switch(info, + partial=df[c("name", "size", "isdir")], + name=df$name, + all= + { + nms <- names(df) + firstcols <- match(c("name", "size", "isdir"), nms) + df[c(firstcols, setdiff(seq_along(nms), firstcols))] + } + ) + }, + + download_file=function(src, dest=basename(src), overwrite=FALSE) + { + force(dest) + src <- curl::curl_escape(sub("^/", "", src)) + path <- paste0("root:/", src, ":/content") + res <- self$do_operation(path, config=httr::write_disk(dest, overwrite=overwrite)) + invisible(res) + }, + + upload_file=function(src, dest, blocksize=32768000) + { + dest <- curl::curl_escape(sub("^/", "", dest)) + path <- paste0("root:/", dest, ":/createUploadSession") + + con <- file(src, open="rb") + on.exit(close(con)) + size <- file.size(src) + + upload_dest <- self$do_operation(path, http_verb="POST")$uploadUrl + next_blockstart <- 0 + next_blockend <- size - 1 + repeat + { + next_blocksize <- min(next_blockend - next_blockstart + 1, blocksize) + seek(con, next_blockstart) + body <- readBin(con, "raw", next_blocksize) + thisblock <- length(body) + if(thisblock == 0) + break + + headers <- httr::add_headers( + `Content-Length`=thisblock, + `Content-Range`=sprintf("bytes %.0f-%.0f/%.0f", + next_blockstart, next_blockstart + next_blocksize - 1, size) + ) + res <- httr::PUT(upload_dest, headers, body=body) + httr::stop_for_status(res) + + next_block <- parse_upload_range(httr::content(res), blocksize) + if(is.null(next_block)) + break + next_blockstart <- next_block[1] + next_blockend <- next_block[2] + } + invisible(ms_drive_item$new(self$token, self$tenant, httr::content(res))) + }, + + create_folder=function(path) + { + name <- basename(path) + parent <- dirname(path) + op <- if(parent %in% c(".", "/")) # assume root + "root/children" + else paste0("root:/", sub("^/", "", parent), ":/children") + parent <- curl::curl_escape(sub("^/", "", dirname(path))) + body <- list( + name=name, + folder=named_list(), + `@microsoft.graph.conflictBehavior`="fail" + ) + res <- self$do_operation(op, body=body, http_verb="POST") + ms_drive_item$new(self$token, self$tenant, res) + }, + + delete_item=function(path, confirm=TRUE) + { + self$get_file_properties(path)$delete(confirm=confirm) + }, + + get_item_properties=function(path) + { + op <- if(path == "/") "root" else file.path("root:", utils::URLencode(path)) + ms_drive_item$new(self$token, self$tenant, self$do_operation(op)) + }, + + set_item_properties=function(path, ...) + { + op <- if(path == "/") "root" else file.path("root:", utils::URLencode(path)) + res <- self$do_operation(op, body=list(...), http_verb="PATCH") + invisible(ms_drive_item$new(self$token, self$tenant, res)) + }, + + print=function(...) + { + personal <- self$properties$driveType == "personal" + name <- if(personal) + paste0("\n") + else paste0("\n") + cat(name) + cat(" directory id:", self$properties$id, "\n") + if(!personal) + { + cat(" web link:", self$properties$webUrl, "\n") + cat(" description:", self$properties$description, "\n") + } + cat("---\n") + cat(format_public_methods(self)) + invisible(self) + } +)) + + +parse_upload_range <- function(response, blocksize) +{ + if(is.null(response$nextExpectedRanges)) + return(NULL) + + x <- as.numeric(strsplit(response$nextExpectedRanges[[1]], "-", fixed=TRUE)[[1]]) + if(length(x) == 1) + x[2] <- x[1] + blocksize - 1 + x +} diff --git a/R/ms_drive_item.R b/R/ms_drive_item.R new file mode 100644 index 0000000..51e90bc --- /dev/null +++ b/R/ms_drive_item.R @@ -0,0 +1,69 @@ +#' File or folder in a drive +#' +#' Class representing an item in a personal OneDrive or SharePoint document library. +#' +#' @docType class +#' @section Fields: +#' - `token`: The token used to authenticate with the Graph host. +#' - `tenant`: The Azure Active Directory tenant for the parent drive. +#' - `type`: always "drive item" for a drive item object. +#' - `properties`: The item properties (metadata). +#' @section Methods: +#' - `new(...)`: Initialize a new object. Do not call this directly; see 'Initialization' below. +#' - `delete(confirm=TRUE)`: Delete this item. By default, ask for confirmation first. +#' - `update(...)`: Update the item's properties in Microsoft Graph. +#' - `do_operation(...)`: Carry out an arbitrary operation on the item. +#' - `sync_fields()`: Synchronise the R object with the item metadata in Microsoft Graph. +#' +#' @section Initialization: +#' Creating new objects of this class should be done via the `get_item_properties` method of the [ms_drive] class. Calling the `new()` method for this class only constructs the R object; it does not call the Microsoft Graph API to retrieve or create the actual item. +#' +#' @seealso +#' [ms_graph], [ms_site], [ms_drive] +#' +#' [Microsoft Graph overview](https://docs.microsoft.com/en-us/graph/overview), +#' [REST API reference](https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0) +#' +#' @examples +#' \dontrun{ +#' +#' # personal OneDrive +#' gr2 <- get_graph_login("consumers") +#' me <- gr2$get_user() +#' mydrv <- me$get_drive() +#' +#' myfile <- drv$get_item_properties("myfile") +#' myfile$properties +#' +#' # rename a file +#' myfile$update(name="newname") +#' +#' # delete the file (will ask for confirmation first) +#' myfile$delete() +#' +#' } +#' @format An R6 object of class `ms_drive_item`, inheriting from `az_object`. +#' @export +ms_drive_item <- R6::R6Class("ms_drive_item", inherit=ms_object, + +public=list( + + initialize=function(token, tenant=NULL, properties=NULL) + { + self$type <- "drive item" + private$api_type <- file.path("drives", properties$parentReference$driveId, "items") + super$initialize(token, tenant, properties) + }, + + print=function(...) + { + file_or_dir <- if(!is.null(self$properties$folder)) "file folder" else "file" + cat("\n", sep="") + cat(" directory id:", self$properties$id, "\n") + cat(" web link:", self$properties$webUrl, "\n") + cat(" type:", file_or_dir, "\n") + cat("---\n") + cat(format_public_methods(self)) + invisible(self) + } +)) diff --git a/R/ms_sharepoint_list.R b/R/ms_sharepoint_list.R new file mode 100644 index 0000000..7ced81c --- /dev/null +++ b/R/ms_sharepoint_list.R @@ -0,0 +1,54 @@ +#' @export +ms_sharepoint_list <- R6::R6Class("ms_sharepoint_list", inherit=ms_object, + +public=list( + + initialize=function(token, tenant=NULL, properties=NULL) + { + self$type <- "list" + private$api_type <- "lists" + super$initialize(token, tenant, properties) + }, + + list_items=function(filter=NULL, select=NULL, include_metadata=FALSE, pagesize=5000) + { + select <- if(is.null(select)) + "fields" + else paste0("fields(select=", paste0(select, collapse=","), ")") + options <- list(expand=select, filter=filter, `$top`=pagesize) + headers <- httr::add_headers(Prefer="HonorNonIndexedQueriesWarningMayFailRandomly") + + items <- self$do_operation("items", options=options, headers) + df <- jsonlite::fromJSON(jsonlite::toJSON(private$get_paged_list(items), auto_unbox=TRUE, null="null")) + if(!include_metadata) + df$fields + else df + }, + + get_column_info=function() + { + res <- self$do_operation(options=list(expand="columns"), simplify=TRUE) + res$columns + }, + + do_operation=function(op="", ...) + { + op <- construct_path( + "sites", self$properties$parentReference$siteId, + "lists", self$properties$id, + op + ) + call_graph_endpoint(self$token, op, ...) + }, + + print=function(...) + { + cat("\n", sep="") + cat(" directory id:", self$properties$id, "\n") + cat(" web link:", self$properties$webUrl, "\n") + cat(" description:", self$properties$description, "\n") + cat("---\n") + cat(format_public_methods(self)) + invisible(self) + } +)) diff --git a/R/ms_site.R b/R/ms_site.R new file mode 100644 index 0000000..a1da7d5 --- /dev/null +++ b/R/ms_site.R @@ -0,0 +1,102 @@ +#' Office 365 SharePoint site +#' +#' Class representing a SharePoint site. +#' +#' @docType class +#' @section Fields: +#' - `token`: The token used to authenticate with the Graph host. +#' - `tenant`: The Azure Active Directory tenant for this site. +#' - `type`: always "site" for a site object. +#' - `properties`: The site properties. +#' @section Methods: +#' - `new(...)`: Initialize a new site object. Do not call this directly; see 'Initialization' below. +#' - `delete(confirm=TRUE)`: Delete a site. By default, ask for confirmation first. +#' - `update(...)`: Update the site metadata in Microsoft Graph. +#' - `do_operation(...)`: Carry out an arbitrary operation on the site. +#' - `sync_fields()`: Synchronise the R object with the site metadata in Microsoft Graph. +#' - `list_drives()`: List the shared document libraries associated with this site. +#' - `get_drive(drive_id)`: Retrieve a shared document library for this site. If the ID is not specified, this returns the default document library. +#' - `list_subsites()`: List the subsites of this site. +#' +#' @section Initialization: +#' Creating new objects of this class should be done via the `get_sharepoint_site` method of the [ms_graph] or [az_group] classes. Calling the `new()` method for this class only constructs the R object; it does not call the Microsoft Graph API to retrieve or create the actual site. +#' +#' @seealso +#' [ms_graph], [ms_drive], [az_user] +#' +#' [Microsoft Graph overview](https://docs.microsoft.com/en-us/graph/overview), +#' [REST API reference](https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0) +#' +#' @examples +#' \dontrun{ +#' +#' gr <- get_graph_login() +#' +#' # a sample site +#' site <- gr$get_sharepoint_site("https://contoso.sharepoint.com/sites/O365-UG-123456") +#' site$list_drives() +#' site$get_drive() +#' +#' } +#' @format An R6 object of class `ms_site`, inheriting from `az_object`. +#' @export +ms_site <- R6::R6Class("ms_site", inherit=ms_object, + +public=list( + + initialize=function(token, tenant=NULL, properties=NULL) + { + self$type <- "site" + private$api_type <- "sites" + super$initialize(token, tenant, properties) + }, + + list_drives=function() + { + res <- private$get_paged_list(self$do_operation("drives")) + private$init_list_objects(res, "drive") + }, + + get_drive=function(drive_id=NULL) + { + op <- if(is.null(drive_id)) + "drive" + else file.path("drives", drive_id) + ms_drive$new(self$token, self$tenant, self$do_operation(op)) + }, + + list_subsites=function() + { + res <- private$get_paged_list(self$do_operation("sites")) + private$init_list_objects(res, "site") + }, + + get_lists=function() + { + res <- private$get_paged_list(self$do_operation("lists")) + private$init_list_objects(res, "list") + }, + + get_list=function(list_name=NULL, list_id=NULL) + { + op <- if(is.null(list_name) && !is.null(list_id)) + file.path("lists", list_id) + else if(!is.null(list_name) && is.null(list_id)) + file.path("lists", curl::curl_escape(list_name)) + else stop("Must supply either site ID or URL") + + res <- self$do_operation(op) + ms_sharepoint_list$new(self$token, self$tenant, res) + }, + + print=function(...) + { + cat("\n", sep="") + cat(" directory id:", self$properties$id, "\n") + cat(" web link:", self$properties$webUrl, "\n") + cat(" description:", self$properties$description, "\n") + cat("---\n") + cat(format_public_methods(self)) + invisible(self) + } +)) diff --git a/man/ms_drive.Rd b/man/ms_drive.Rd new file mode 100644 index 0000000..170e459 --- /dev/null +++ b/man/ms_drive.Rd @@ -0,0 +1,102 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ms_drive.R +\docType{class} +\name{ms_drive} +\alias{ms_drive} +\title{Personal OneDrive or SharePoint document library} +\format{ +An R6 object of class \code{ms_drive}, inheriting from \code{ms_object}. +} +\description{ +Class representing a personal OneDrive or SharePoint document library. +} +\section{Fields}{ + +\itemize{ +\item \code{token}: The token used to authenticate with the Graph host. +\item \code{tenant}: The Azure Active Directory tenant for this drive. +\item \code{type}: always "drive" for a drive object. +\item \code{properties}: The drive properties. +} +} + +\section{Methods}{ + +\itemize{ +\item \code{new(...)}: Initialize a new drive object. Do not call this directly; see 'Initialization' below. +\item \code{delete(confirm=TRUE)}: Delete a drive. By default, ask for confirmation first. +\item \code{update(...)}: Update the drive metadata in Microsoft Graph. +\item \code{do_operation(...)}: Carry out an arbitrary operation on the drive. +\item \code{sync_fields()}: Synchronise the R object with the drive metadata in Microsoft Graph. +\item \code{list_items(path, info, full.names)}: List the files and folders under the specified path. See 'File and folder operations' below. +\item \code{download_file(src, dest, overwrite)}: Download a file. +\item \code{upload_file(src, dest, blocksize)}: Upload a file. +\item \code{create_folder(path)}: Create a folder. +\item \code{delete_item(path, confirm)}: Delete a file or folder. +\item \code{get_item_properties(path)}: Get the properties (metadata) for a file or folder. +\item \code{set_item_properties(path, ...)}: Set the properties for a file or folder. +} +} + +\section{Initialization}{ + +Creating new objects of this class should be done via the \code{get_drive} methods of the \link{ms_graph}, \link{az_user} or \link{ms_site} classes. Calling the \code{new()} method for this class only constructs the R object; it does not call the Microsoft Graph API to retrieve or create the actual drive. +} + +\section{File and folder operations}{ + +This class exposes methods for carrying out common operations on files and folders. + +\code{list_items} lists the items under the specified path. It is the analogue of base R's \code{dir}/\code{list.files}. The arguments are +\itemize{ +\item \code{path}: The path. +\item \code{info}: The information to return: either "partial", "name" or "all". If "partial", a data frame is returned containing the name, size and whether the item is a file or folder. If "name", a vector of file/folder names is returned. If "all", a data frame is returned containing \emph{all} the properties for each item (this can be large). +\item \code{full.names}: Whether to prefix the full path to the names of the items. +} + +\code{download_file} and \code{upload_file} download and upload files from the local machine to the drive. For \code{upload_file}, the uploading is done in blocks of 32MB by default; you can change this by setting the \code{blocksize} argument. For technical reasons, the block size \href{https://docs.microsoft.com/en-us/graph/api/driveitem-createuploadsession?view=graph-rest-1.0#upload-bytes-to-the-upload-session}{must be a multiple of 320KB}. + +\code{create_folder} creates a folder with the specified path. Trying to create an already existing folder is an error. + +\code{delete_item} deletes a file or folder. By default, it will ask for confirmation first. + +\code{get_item_properties} returns an object of \link{ms_drive_item}, containing the properties (metadata) for a given file or folder. The properties can be found in the \code{properties} field of this object. + +\code{set_item_properties} sets the properties (metadata) of a file or folder. The new properties should be specified as individual named arguments to the method. Any existing properties that aren't listed as arguments will retain their previous values or be recalculated based on changes to other properties, as appropriate. +} + +\examples{ +\dontrun{ + +gr <- get_graph_login() + +# shared document library for a SharePoint site +site <- gr$get_sharepoint_site("https://contoso.sharepoint.com/sites/O365-UG-123456") +drv <- site$get_drive() + +# personal OneDrive +gr2 <- get_graph_login("consumers") +me <- gr2$get_user() +mydrv <- me$get_drive() + +## file/folder operationss +drv$list_items() +drv$list_items("path/to/folder", full.names=TRUE) + +# download a file -- default destination filename is taken from the source +drv$download_file("path/to/folder/data.csv") + +myfile <- drv$get_item_properties("myfile") +myfile$properties + +# rename a file +drv$set_item_properties("myfile", name="newname") + +} +} +\seealso{ +\link{ms_graph}, \link{ms_site}, \link{ms_drive_item} + +\href{https://docs.microsoft.com/en-us/graph/overview}{Microsoft Graph overview}, +\href{https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0}{REST API reference} +} diff --git a/man/ms_drive_item.Rd b/man/ms_drive_item.Rd new file mode 100644 index 0000000..fffa239 --- /dev/null +++ b/man/ms_drive_item.Rd @@ -0,0 +1,63 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ms_drive_item.R +\docType{class} +\name{ms_drive_item} +\alias{ms_drive_item} +\title{File or folder in a drive} +\format{ +An R6 object of class \code{ms_drive_item}, inheriting from \code{az_object}. +} +\description{ +Class representing an item in a personal OneDrive or SharePoint document library. +} +\section{Fields}{ + +\itemize{ +\item \code{token}: The token used to authenticate with the Graph host. +\item \code{tenant}: The Azure Active Directory tenant for the parent drive. +\item \code{type}: always "drive item" for a drive item object. +\item \code{properties}: The item properties (metadata). +} +} + +\section{Methods}{ + +\itemize{ +\item \code{new(...)}: Initialize a new object. Do not call this directly; see 'Initialization' below. +\item \code{delete(confirm=TRUE)}: Delete this item. By default, ask for confirmation first. +\item \code{update(...)}: Update the item's properties in Microsoft Graph. +\item \code{do_operation(...)}: Carry out an arbitrary operation on the item. +\item \code{sync_fields()}: Synchronise the R object with the item metadata in Microsoft Graph. +} +} + +\section{Initialization}{ + +Creating new objects of this class should be done via the \code{get_item_properties} method of the \link{ms_drive} class. Calling the \code{new()} method for this class only constructs the R object; it does not call the Microsoft Graph API to retrieve or create the actual item. +} + +\examples{ +\dontrun{ + +# personal OneDrive +gr2 <- get_graph_login("consumers") +me <- gr2$get_user() +mydrv <- me$get_drive() + +myfile <- drv$get_item_properties("myfile") +myfile$properties + +# rename a file +myfile$update(name="newname") + +# delete the file (will ask for confirmation first) +myfile$delete() + +} +} +\seealso{ +\link{ms_graph}, \link{ms_site}, \link{ms_drive} + +\href{https://docs.microsoft.com/en-us/graph/overview}{Microsoft Graph overview}, +\href{https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0}{REST API reference} +} diff --git a/man/ms_site.Rd b/man/ms_site.Rd new file mode 100644 index 0000000..a543754 --- /dev/null +++ b/man/ms_site.Rd @@ -0,0 +1,59 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/ms_site.R +\docType{class} +\name{ms_site} +\alias{ms_site} +\title{Office 365 SharePoint site} +\format{ +An R6 object of class \code{ms_site}, inheriting from \code{az_object}. +} +\description{ +Class representing a SharePoint site. +} +\section{Fields}{ + +\itemize{ +\item \code{token}: The token used to authenticate with the Graph host. +\item \code{tenant}: The Azure Active Directory tenant for this site. +\item \code{type}: always "site" for a site object. +\item \code{properties}: The site properties. +} +} + +\section{Methods}{ + +\itemize{ +\item \code{new(...)}: Initialize a new site object. Do not call this directly; see 'Initialization' below. +\item \code{delete(confirm=TRUE)}: Delete a site. By default, ask for confirmation first. +\item \code{update(...)}: Update the site metadata in Microsoft Graph. +\item \code{do_operation(...)}: Carry out an arbitrary operation on the site. +\item \code{sync_fields()}: Synchronise the R object with the site metadata in Microsoft Graph. +\item \code{list_drives()}: List the shared document libraries associated with this site. +\item \code{get_drive(drive_id)}: Retrieve a shared document library for this site. If the ID is not specified, this returns the default document library. +\item \code{list_subsites()}: List the subsites of this site. +} +} + +\section{Initialization}{ + +Creating new objects of this class should be done via the \code{get_sharepoint_site} method of the \link{ms_graph} or \link{az_group} classes. Calling the \code{new()} method for this class only constructs the R object; it does not call the Microsoft Graph API to retrieve or create the actual site. +} + +\examples{ +\dontrun{ + +gr <- get_graph_login() + +# a sample site +site <- gr$get_sharepoint_site("https://contoso.sharepoint.com/sites/O365-UG-123456") +site$list_drives() +site$get_drive() + +} +} +\seealso{ +\link{ms_graph}, \link{ms_drive}, \link{az_user} + +\href{https://docs.microsoft.com/en-us/graph/overview}{Microsoft Graph overview}, +\href{https://docs.microsoft.com/en-us/graph/api/overview?view=graph-rest-1.0}{REST API reference} +}