I want to create folders and add classes within the folders. I can create the folder once, but once I have created it, I just want to add classes. My code gives error because I'm trying to create a folder several times, which is not right. So before adding a class to the folder, I want to check if the folder already exists and if it exists I just want to add the class.
ProjectItem rootFolder = project.ProjectItems.AddFolder(folder);
ProjectItem item = rootFolder.ProjectItems.AddFromTemplate(itemPath, className);
According to documentation, there is no kind of Exists function which would tell us if a folder already existed.
So you have at least two options:
1. Try and ignore
Simply:
try
{
var rootFolder = project.ProjectItems.AddFolder(folder);
}
catch
{
/* folder already exists, nothing to do */
}
2. Solution folders can only occur in the first level below the solution root node, so we can get away with a non-recursive solution.
public static bool CheckIfSolutionFolderExists(ProjectItems projectItems, string foldername)
{
foreach(var projectItem in projectItems)
{
if(projectItem.Kind == EnvDTE.vsProjectItemKindVirtualFolder)
{
if(projectItem.Name == foldername)
{
return true;
}
}
}
return false;
}
For a recursive solution, I found this, which boils down to:
public static bool CheckIfFileExistsInProject(ProjectItems projectItems, string fullpath)
{
foreach(ProjectItem projectItem in projectItems)
{
if(projectItem.Name == fullpath)
{
return true;
}
else if ((projectItem.ProjectItems != null) && (projectItem.ProjectItems.Count > 0))
{
/* recursive search */
return CheckIfFileExistsInProject(projectItem.ProjectItems, fullpath);
}
}
return false;
}
A robust pattern for solution and project folder management would use AddFromDirectory whenever you want to mirror the file system hierarchy in the project tree, and AddFolder only for virtual folders that have no representation in the file system.
ProjectItem item;
if(Directory.Exists(itemname))
{
item = project.AddFromDirectory(itemname);
}
else
{
item = project.AddFolder(itemname);
}
(source: inspired from this)
You can use method Directory.Exists
and in your code it'll be look like there
if(!Directory.Exists(folder)) {
ProjectItem rootFolder = project.ProjectItems.AddFolder(folder);
}
ProjectItem item = rootFolder.ProjectItems.AddFromTemplate(itemPath, className);
You need to do the following:
if(Directory.Exists(path))
{
// The folder already exists
}
else
{
//Create a new folder here ...
}
Please check this out for more details on Directory.Exists
Related
I am trying to create a microservice in C# which will accept a csv file containing order numbers, digest the csv, connect to sharepoint, create a new folder on sharepoint, and then copy contracts with names corresponding to the order number from whereever they may be (and they probably won't all be in the smae place) to the new folder.
At this point, with help from Stackoverflow, I can successfully get an authentication token from our Sharepoint using a CSOM Authentication Manager. And now I am trying to figure out how to create a the folder. Googling for information on creating Sharepoint folders keeps bringing up the topic of lists, which I don't know anything about and don't even know if I really want or need to know anything about, or whether there might be a different way which works with the site as that's what I'm actually interested in.
So, let's say I have a sharepoint site at https://example.sharepoint.com/sites/MySite.
How can I simply create a folder called "Foo" within a folder called "Bar" which exists in "Shared Documents"?
If I need to know something about lists in order to do this, can I use C# to find the correct list? Or do I need to chase my adminstrator for additional information?
Assuming the AuthenticationManager returns a valid context and the root folder already exists, the following works:
using Microsoft.SharePoint.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using AuthenticationManager = SharepointOrderContractExtractor.Clients.AuthenticationManager;
namespace SharePointOrderContractExtractor.Clients
{
public class FolderManager
{
private readonly AuthenticationManager _authenticationManager;
public FolderManager(
AuthenticationManager sharepointAuthenticationManager
)
{
_authenticationManager = sharepointAuthenticationManager;
}
internal Folder EnsureAndGetTargetFolder(string folderPath)
{
using ClientContext context = _authenticationManager.GetContext();
List<string> folderNames = folderPath.Split("/").ToList();
List documents = context.Web.Lists.GetByTitle(folderNames[0]);
folderNames.RemoveAt(0);
return EnsureAndGetTargetFolder(context, documents, folderNames);
}
private Folder EnsureAndGetTargetFolder(ClientContext context, List list, List<string> folderPath)
{
Folder returnFolder = list.RootFolder;
return (folderPath != null && folderPath.Count > 0)
? EnsureAndGetTargetSubfolder(context, list, folderPath)
: returnFolder;
}
private Folder EnsureAndGetTargetSubfolder(ClientContext context, List list, List<string> folderPath)
{
Web web = context.Web;
Folder currentFolder = list.RootFolder;
context.Load(web, t => t.Url);
context.Load(currentFolder);
context.ExecuteQuery();
foreach (string folderPointer in folderPath)
{
currentFolder = FindOrCreateFolder(context, list, currentFolder, folderPointer);
}
return currentFolder;
}
private Folder FindOrCreateFolder(ClientContext context, List list, Folder currentFolder, string folderPointer)
{
FolderCollection folders = currentFolder.Folders;
context.Load(folders);
context.ExecuteQuery();
foreach (Folder existingFolder in folders)
{
if (existingFolder.Name.Equals(folderPointer, StringComparison.InvariantCultureIgnoreCase))
{
return existingFolder;
}
}
return CreateFolder(context, list, currentFolder, folderPointer);
}
private Folder CreateFolder(ClientContext context, List list, Folder currentFolder, string folderPointer)
{
ListItemCreationInformation itemCreationInfo = new ListItemCreationInformation
{
UnderlyingObjectType = FileSystemObjectType.Folder,
LeafName = folderPointer,
FolderUrl = currentFolder.ServerRelativeUrl
};
ListItem folderItemCreated = list.AddItem(itemCreationInfo);
folderItemCreated.Update();
context.Load(folderItemCreated, f => f.Folder);
context.ExecuteQuery();
return folderItemCreated.Folder;
}
}
}
I am trying to find subsolution and add items into that solution programmatically. But code that i use is not searching for subfolders.
I use code on github: See code here please
Method that i search for solution is:
public static IEnumerable<EnvDTE.Project> GetAllProjects(EnvDTE.DTE dte)
{
List<EnvDTE.Project> projectList = new List<EnvDTE.Project>();
var folders = dte.Solution.Projects.Cast<EnvDTE.Project>().Where(p=>p.Kind == EnvDTE80.ProjectKinds.vsProjectKindSolutionFolder);
foreach (EnvDTE.Project folder in folders)
{
if (folder.ProjectItems == null) continue;
foreach (EnvDTE.ProjectItem item in folder.ProjectItems)
{
if (item.Object is EnvDTE.Project)
projectList.Add(item.Object as EnvDTE.Project);
}
}
var projects = dte.Solution.Projects.Cast<EnvDTE.Project>().Where(p=>p.Kind != EnvDTE80.ProjectKinds.vsProjectKindSolutionFolder);
if (projects.Count() > 0)
projectList.AddRange(projects);
return projectList;
}
But this is searching only main solution and sub solution. But solution that i want to find has a longer path like "MainFolder\SubFolder1\SubFolder2\SubFolder3\SubSolution"
I want to get from this
"../lib/../data/myFile.xml"
to this
"../data/myFile.xml"
I guess I could do it by manipulating the string, searching for "../" and canceling them out with the preceding folders but I was looking for an already existing C# solution.
Tried instantiating an Uri from this string and going back toString(). Didn't help. It leaves the string unchanged.
You can always try to use:
Path.GetFullPath("../lib/../data/myFile.xml")
It behaves as you want with absolute paths but you might end up with strange behaviors with relative paths since it always bases itself from the current working directory. For instance:
Path.GetFullPath("/lib/../data/myFile.xml") // C:\data\myFile.xml
Path.GetFullPath("../lib/../data/myFile.xml") // C:\Program Files (x86)\data\myFile.xml
Sounds like you may either need to parse/rebuild the path yourself, or use some kind of well constructed regular expression to do this for you.
Taking the parse/rebuild route, you could do something like:
public static string NormalisePath(string path)
{
var components = path.Split(new Char[] {'/'});
var retval = new Stack<string>();
foreach (var bit in components)
{
if (bit == "..")
{
if (retval.Any())
{
var popped = retval.Pop();
if (popped == "..")
{
retval.Push(popped);
retval.Push(bit);
}
}
else
{
retval.Push(bit);
}
}
else
{
retval.Push(bit);
}
}
var final = retval.ToList();
final.Reverse();
return string.Join("/", final.ToArray());
}
(and yes, you'd probably want better variable names/commenting/etc.)
You can use a regular expression to do this:
public static string NormalisePath(string path)
{
return new Regex(#"\.{2}/.*/(?=\.\.)").Replace(path, "");
}
On my FTP Server I have the following folder structure
- Parent Directory
-a.txt
-b.txt.old
-SubDirectory1
-c.txt
-NestedSubDirectory1
-d.txt
-SubDirectory2
-e.txt
-f.txt.old
The number of SDs are not fixed. I need a way to get all the files(can be any format) without the .old extension from the Parent Directory.
I'm currently using the 3rd party dll edtFTPnet.
ftpConnection.GetFileInfos()Where(f => !(f.Name.EndsWith(".old"))).ToList();
This helps me get the details of the files and folders at the current working directory level.
Can someone tell me a way to get all the files with the parentdirectory, subdirectories and nested subdirectories.
The solution may or may not use edtFTPnet.
FTPConnection.GetFileInfos() returns an array of FTPFile. The class FTPFile has a boolean property Dir which indicates whether its filename accesses a file (false) or directory (true).
Something like this should work:
void ReadSubDirectories(FTPConncetion connection, FTPFile[] files)
{
foreach (var file in files)
{
if (file.Dir)
{
// Save parent directory
var curDir = connection.ServerDirectory;
// Move into directory
connection.ChangeWorkingDirectory(file.Name)
// Read all files
ReadSubDirectories(connection, connection.GetFileInfos());
// Move back into parent directory
connection.ChangeWorkingDirectory(curDir)
}
else
{
// Do magic with your files
}
}
}
However you might be better off using just .NET's built-in FtpWebRequest class since its methods and naming conventions are clearer, it's better documented and it's easier to find references online.
Try to use extensions like this:
class Program
{
static void Main(string[] args)
{
using (var connection = new FTPConnection
{
ServerAddress = "127.0.0.1",
UserName = "Admin",
Password = "1",
})
{
connection.Connect();
connection.ServerDirectory = "/recursive_folder";
var resultRecursive =
connection.GetFileInfosRecursive().Where(f => !(f.Name.EndsWith(".old"))).ToList();
var resultDefault = connection.GetFileInfos().Where(f => !(f.Name.EndsWith(".old"))).ToList();
}
}
}
public static class FtpClientExtensions
{
public static FTPFile[] GetFileInfosRecursive(this FTPConnection connection)
{
var resultList = new List<FTPFile>();
var fileInfos = connection.GetFileInfos();
resultList.AddRange(fileInfos);
foreach (var fileInfo in fileInfos)
{
if (fileInfo.Dir)
{
connection.ServerDirectory = fileInfo.Path;
resultList.AddRange(connection.GetFileInfosRecursive());
}
}
return resultList.ToArray();
}
}
In Sharepoint how can you copy a list item from one list to another list
eg copy from "List A" to "List B" (both are at the root of the site)
I want this copying to occur when a new list item is added to "List A"
I tried using the CopyTo() method of an SPListItem inside the ItemAdded event receiver but couldnt figure out the url to copy to.
Here is the code I use. Pass it a SPlistItem and the name of the destination list as seen in Sharepoint(Not the URL). The only restriction is that both list must be in the same site:
private SPListItem CopyItem(SPListItem sourceItem, string destinationListName) {
//Copy sourceItem to destinationList
SPList destinationList = sourceItem.Web.Lists[destinationListName];
SPListItem targetItem = destinationList.Items.Add();
foreach (SPField f in sourceItem.Fields) {
//Copy all except attachments.
if (!f.ReadOnlyField && f.InternalName != "Attachments"
&& null != sourceItem[f.InternalName])
{
targetItem[f.InternalName] = sourceItem[f.InternalName];
}
}
//Copy attachments
foreach (string fileName in sourceItem.Attachments) {
SPFile file = sourceItem.ParentList.ParentWeb.GetFile(sourceItem.Attachments.UrlPrefix + fileName);
byte[] imageData = file.OpenBinary();
targetItem.Attachments.Add(fileName, imageData);
}
return targetItem;
}
Indeed as Lars said, it can be tricky to move items and retain versions and correct userinfo. I have done similar things with that before so if you need some code examples, let me know through a comment and can supply you with some guidance.
The CopyTo method (if you decide to go with that) need an absolute Uri like:
http://host/site/web/list/filename.doc
So, if you are performing this in an event receiver you need to concatinate a string containing the elements needed. Something like (note that this can be done in other ways to):
string dest=
siteCollection.Url + "/" + site.Name + list.Name + item.File.Name;
Copying and moving files, items and folders in SharePoint can be tricky if you want to retain all metadata, timestamps, author info and version history. Take a look a CopyMove for SharePoint - it also has a Web Service API.
There's many tools on the market for copying a list item to another list (avepoint, metavis, etc.) but they are pretty expensive if you're planning to do this on only one list.
If you can do this manually once a week for example, look at the following tool : http://en.share-gate.com/sharepoint-tools/copy-move-sharepoint-list-items-with-metadata-and-version-history
Here is a powershell equivalent of Sylvian's that does allow for cross-site copy. His code could be modified similarly as well...
param([string]$sourceWebUrl, [string]$sourceListName, [string]$destWebUrl, [string]$destListName)
$sourceWeb = get-spweb $sourceWebUrl;
$sourceList = $sourceWeb.Lists[$sourceListName];
$destWeb = get-spweb $destWebUrl;
$destList = $destWeb.Lists[$destListName];
$sourceList.Items |%{
$destItem = $destList.Items.Add();
$sourceItem = $_;
$sourceItem.Fields |%{
$f = $_;
if($f.ReadOnlyField -eq $false -and $f.InternalName -ne "Attachments" -and $sourceItem[$f.InternalName] -ne $null){
$destItem[$f.InternalName] = $sourceItem[$f.InternalName];
}
}
$destItem.Update();
}
To use, copy and past to a file copy-listitems.ps1 and run using Sharpoint powerhsell commandline...
Make sure you call CopyTo(url) method on SPFile, not on SPListItem.
for example:
ItemUpdated(SPItemEventProperties properties)
{
//...
string url = properties.Web.Site.Url + "/" + properties.Web.Name + "Lists/ListName/" + properties.ListItem.File.Name;
//properties.ListItem.File.MoveTo(url);
properties.ListItem.File.CopyTo(url);
//...
}
private void CopyAttachmentsToList(SPListItem srcItem, SPListItem tgtItem)
{
try
{
//get source item attachments from the folder
SPFolder srcAttachmentsFolder =
srcItem.Web.Folders["Lists"].SubFolders[srcItem.ParentList.Title].SubFolders["Attachments"].SubFolders[srcItem.ID.ToString()];
//Add items to the target item
foreach (SPFile file in srcAttachmentsFolder.Files)
{
byte[] binFile = file.OpenBinary();
tgtItem.Update();
tgtItem.Attachments.AddNow(file.Name, binFile);
tgtItem.Update();
}
}
catch
{
//exception message goes here
}
finally
{
srcItem.Web.Dispose();
}
}
Don't forget to add this line, tgtItem.Update();, else you will get an err.
So, the lists have the exact same or similar columns? Either way, you could create a simple workflow that runs automatically when an item is created in "List A". Since the workflow in question is relatively simple, I'd recommend using SharePoint Designer (which is free) to create it, since you can easily match up the columns from the two lists. The walk through below should be able to help you get started.
Create a Workflow - SharePoint Designer
I had the same problem.
After experimenting a bit instead of
targetItem[f.InternalName] = sourceItem[f.InternalName];
I used:
targetItem[childField.Title] = sourceItem[parentField.Title];
How to copy field and save versions:
public static SPListItem CopyItem(SPListItem sourceItem, SPList destinationList)
{
SPListItem targetItem = destinationList.AddItem();
//loop over the soureitem, restore it
for (int i = sourceItem.Versions.Count - 1; i >= 0; i--)
{
//set the values into the archive
foreach (SPField sourceField in sourceItem.Fields)
{
SPListItemVersion version = sourceItem.Versions[i];
if ((!sourceField.ReadOnlyField) && (sourceField.InternalName != "Attachments"))
{
SetFields(targetItem, sourceField, version);
}
}
//update the archive item and
//loop over the the next version
targetItem.Update();
}
foreach (string fileName in sourceItem.Attachments)
{
SPFile file = sourceItem.ParentList.ParentWeb.GetFile(sourceItem.Attachments.UrlPrefix + fileName);
targetItem.Attachments.Add(fileName, file.OpenBinary());
}
targetItem.SystemUpdate();
return targetItem;
}
private static bool SetFields(SPListItem targetItem, SPField sourceField, SPListItemVersion version)
{
try
{
targetItem[sourceField.InternalName] = version.ListItem[sourceField.InternalName];
return true;
}
catch (System.ArgumentException)//field not filled
{
return false;
}
catch (SPException)//field not filled
{
return false;
}
}
Copy List Items from one SharePoint List or library to Another SharePoint list or library using c# server side code
//Itecollection is a collection of data from source list
public void CopyItemsFromOneListToAnotherList(SPListItemCollection itemCollection)
{
using (SPSite site = new SPSite(siteUrl))
{
using (SPWeb web = site.OpenWeb())
{
//Get destination list/library
//destListName - Destination list/library name
SPList destList = web.Lists.TryGetList(destListName);
foreach (SPListItem sourceItem in itemCollection)
{
//Add new Item to list
SPListItem destItem = destList.Items.Add();
foreach (SPField field in sourceItem.Fields)
{
if (!field.ReadOnlyField && !field.Hidden && field.InternalName != "Attachments")
{
if (destItem.Fields.ContainsField(field.InternalName))
{
//Copy item to destination library
destItem[field.InternalName] = sourceItem[field.InternalName];
}
}
}
//Update item in destination library or list
destItem.Update();
Console.WriteLine("Copied " + sourceItem["ID"] + "to destination list/library");
}
}
}
}