package utils import ( "context" "net/url" "strings" "github.com/alcionai/clues" "github.com/spf13/cobra" "github.com/alcionai/corso/src/cli/flags" "github.com/alcionai/corso/src/pkg/logger" "github.com/alcionai/corso/src/pkg/selectors" ) type SharePointOpts struct { SiteID []string WebURL []string Library string FileName []string // for libraries, to duplicate onedrive interface FolderPath []string // for libraries, to duplicate onedrive interface FileCreatedAfter string FileCreatedBefore string FileModifiedAfter string FileModifiedBefore string Lists []string ListModifiedAfter string ListModifiedBefore string ListCreatedBefore string ListCreatedAfter string PageFolder []string Page []string RestoreCfg RestoreCfgOpts ExportCfg ExportCfgOpts Populated flags.PopulatedFlags } func (s SharePointOpts) GetFileTimeField(flag string) string { switch flag { case flags.FileCreatedAfterFN: return s.FileCreatedAfter case flags.FileCreatedBeforeFN: return s.FileCreatedBefore case flags.FileModifiedAfterFN: return s.FileModifiedAfter case flags.FileModifiedBeforeFN: return s.FileModifiedBefore case flags.ListModifiedAfterFN: return s.ListModifiedAfter case flags.ListModifiedBeforeFN: return s.ListModifiedBefore case flags.ListCreatedBeforeFN: return s.ListCreatedBefore case flags.ListCreatedAfterFN: return s.ListCreatedAfter default: return "" } } func MakeSharePointOpts(cmd *cobra.Command) SharePointOpts { return SharePointOpts{ SiteID: flags.SiteIDFV, WebURL: flags.WebURLFV, Library: flags.LibraryFV, FileName: flags.FileNameFV, FolderPath: flags.FolderPathFV, FileCreatedAfter: flags.FileCreatedAfterFV, FileCreatedBefore: flags.FileCreatedBeforeFV, FileModifiedAfter: flags.FileModifiedAfterFV, FileModifiedBefore: flags.FileModifiedBeforeFV, Lists: flags.ListFV, ListModifiedAfter: flags.ListModifiedAfterFV, ListModifiedBefore: flags.ListModifiedBeforeFV, ListCreatedAfter: flags.ListCreatedAfterFV, ListCreatedBefore: flags.ListCreatedBeforeFV, Page: flags.PageFV, PageFolder: flags.PageFolderFV, RestoreCfg: makeRestoreCfgOpts(cmd), ExportCfg: makeExportCfgOpts(cmd), // populated contains the list of flags that appear in the // command, according to pflags. Use this to differentiate // between an "empty" and a "missing" value. Populated: flags.GetPopulatedFlags(cmd), } } func SharePointAllowedCategories() map[string]struct{} { return map[string]struct{}{ flags.DataLibraries: {}, // flags.DataLists: {}, [TODO]: uncomment when lists are enabled } } func AddCategories(sel *selectors.SharePointBackup, cats []string) *selectors.SharePointBackup { if len(cats) == 0 { // backup of sharepoint lists not enabled yet // sel.Include(sel.LibraryFolders(selectors.Any()), sel.Lists(selectors.Any())) sel.Include(sel.LibraryFolders(selectors.Any())) } for _, d := range cats { switch d { // backup of sharepoint lists not enabled yet // case flags.DataLists: // sel.Include(sel.Lists(selectors.Any())) case flags.DataLibraries: sel.Include(sel.LibraryFolders(selectors.Any())) } } return sel } // ValidateSharePointRestoreFlags checks common flags for correctness and interdependencies func ValidateSharePointRestoreFlags(backupID string, opts SharePointOpts) error { if len(backupID) == 0 { return clues.New("a backup ID is required") } // ensure url can parse all weburls provided by --site. if _, ok := opts.Populated[flags.SiteFN]; ok { for _, wu := range opts.WebURL { if _, err := url.Parse(wu); err != nil { return clues.New("invalid site url: " + wu) } } } return validateCommonTimeFlags(opts) } // AddSharePointInfo adds the scope of the provided values to the selector's // filter set func AddSharePointInfo( sel *selectors.SharePointRestore, v string, f func(string) []selectors.SharePointScope, ) { if len(v) == 0 { return } sel.Filter(f(v)) } // IncludeSharePointRestoreDataSelectors builds the common data-selector // inclusions for SharePoint commands. func IncludeSharePointRestoreDataSelectors(ctx context.Context, opts SharePointOpts) *selectors.SharePointRestore { sites := opts.SiteID folderPaths, fileNames := len(opts.FolderPath), len(opts.FileName) siteIDs, webUrls := len(opts.SiteID), len(opts.WebURL) lists := len(opts.Lists) pageFolders, pageItems := len(opts.PageFolder), len(opts.Page) if siteIDs == 0 { sites = selectors.Any() } sel := selectors.NewSharePointRestore(sites) if folderPaths+fileNames+webUrls+lists+pageFolders+pageItems == 0 { sel.Include(sel.AllData()) return sel } if folderPaths+fileNames > 0 { if fileNames == 0 { opts.FileName = selectors.Any() } opts.FolderPath = trimFolderSlash(opts.FolderPath) containsFolders, prefixFolders := splitFoldersIntoContainsAndPrefix(opts.FolderPath) if len(containsFolders) > 0 { sel.Include(sel.LibraryItems(containsFolders, opts.FileName)) } if len(prefixFolders) > 0 { sel.Include(sel.LibraryItems(prefixFolders, opts.FileName, selectors.PrefixMatch())) } } if lists > 0 { opts.Lists = trimFolderSlash(opts.Lists) sel.Include(sel.ListItems(opts.Lists, opts.Lists, selectors.StrictEqualMatch())) sel.Configure(selectors.Config{OnlyMatchItemNames: true}) } if pageFolders+pageItems > 0 { if pageItems == 0 { opts.Page = selectors.Any() } opts.PageFolder = trimFolderSlash(opts.PageFolder) containsFolders, prefixFolders := splitFoldersIntoContainsAndPrefix(opts.PageFolder) if len(containsFolders) > 0 { sel.Include(sel.PageItems(containsFolders, opts.Page)) } if len(prefixFolders) > 0 { sel.Include(sel.PageItems(prefixFolders, opts.Page, selectors.PrefixMatch())) } } if webUrls > 0 { urls := make([]string, 0, len(opts.WebURL)) for _, wu := range opts.WebURL { // for normalization, ensure the site has a https:// prefix. wu = strings.TrimPrefix(wu, "https://") wu = strings.TrimPrefix(wu, "http://") // don't add a prefix to path-only values if len(wu) > 0 && wu != "*" && !strings.HasPrefix(wu, "/") { wu = "https://" + wu } u, err := url.Parse(wu) if err != nil { // shouldn't be possible to err, if we called validation first. logger.Ctx(ctx).With("web_url", wu).Error("malformed web url") continue } urls = append(urls, u.String()) } sel.Include(sel.WebURL(urls)) } return sel } // FilterSharePointRestoreInfoSelectors builds the common info-selector filters. func FilterSharePointRestoreInfoSelectors( sel *selectors.SharePointRestore, opts SharePointOpts, ) { AddSharePointInfo(sel, opts.Library, sel.Library) AddSharePointInfo(sel, opts.FileCreatedAfter, sel.CreatedAfter) AddSharePointInfo(sel, opts.FileCreatedBefore, sel.CreatedBefore) AddSharePointInfo(sel, opts.FileModifiedAfter, sel.ModifiedAfter) AddSharePointInfo(sel, opts.FileModifiedBefore, sel.ModifiedBefore) AddSharePointInfo(sel, opts.ListModifiedAfter, sel.ListModifiedAfter) AddSharePointInfo(sel, opts.ListModifiedBefore, sel.ListModifiedBefore) AddSharePointInfo(sel, opts.ListCreatedAfter, sel.ListCreatedAfter) AddSharePointInfo(sel, opts.ListCreatedBefore, sel.ListCreatedBefore) }