SharePoint - Custom document library with folder structure - sharepoint

I have a custom document library template with content types. This works fine but the only thing that I would like to add is that when a user instantiates a new document library based on that template, that is has a predefined folder structure already in place.
I have tried adding Module tags in my schema.xml but this doesn't seem to work.
I know that it is possible to provision a document library with files and folders with a ListInstance feature but this is not possible in this case. I would like that the predefined folder structure is part of the document library template.
Is this possible?
Thanks
Maarten

If You want to achieve this using Document Library Definition. I don't think that would be achievable. What you can do is take help of list /document library templates.
1 Create a custom Doclibary the way you want.
2. create the desired folder structure. without uploading any documents.
3, Save the doc library as template by going to Doclibray settings ( make sure you store the template along with content stored into it)

Another method (which I must blog on soon) is to fake a list creation event. I add an empty view definition with a custom aspx page to the list template. The custom page simply executes some custom functionality on the list, deletes the initialisation view, then redirects to the normal view. It's a little messy, and it will only work if the list is created through the UI, but it works.
Here is a very quick example. You already have your list template. In the schema.xml file, add a new View to the Views element like so:
<Views>
<!-- Below is a blank view used to kick of initialisation after list creation. -->
<View DisplayName="Initialise" Type="HTML" DefaultView="TRUE" WebPartZoneID="Main" SetupPath="pages\Scratch\init.aspx" Hidden="TRUE" Url="_init.aspx">
<Toolbar Type="Standard" />
<ViewHeader />
<ViewBody />
<ViewFooter />
<ViewEmpty />
<ViewFields />
<ViewData />
<Query />
</View>
<!-- standard views would be here -->
</Views>
You may be able to go without the empty elements in there. That was something I was going to test further before blogging on it. But this will get the job done. The important things are:
This view is the first view and DefaultView is set to TRUE.
The SetupPath is set to a custom page that you will provision with your solution.
For the custom page (init.aspx in my example), I just made a copy of ...\12\TEMPLATE\Pages\viewpage.aspx and changed what the page inherits from. You could do this with inline code, but I used a codebehind assembly. So first line of that file becomes:
<%# Page language="C#" MasterPageFile="~masterurl/default.master" Inherits="SharePointScratch.InitPage,SharePointScratch,Version=1.0.0.0,Culture=neutral,PublicKeyToken=xxxxxxxxxxxxxxxx" %>
And then the codebehind:
using System;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Utilities;
namespace SharePointScratch
{
public class InitPage : System.Web.UI.Page
{
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
SPList list = SPContext.Current.List;
list.ParentWeb.AllowUnsafeUpdates = true;
// Create you folder structure here...
// Fix the views by deleting the initialisation view.
SPView view = SPContext.Current.ViewContext.View;
list.Views.Delete(view.ID);
list.Update();
list.ParentWeb.AllowUnsafeUpdates = false;
// Redirect to the new default view.
SPUtility.Redirect(list.DefaultViewUrl, SPRedirectFlags.Default, this.Context);
}
}
}
Basically, we are relying on the SharePoint default behavior to display the default view of a list after creation. A custom view is inserted in the schema with the sole intention of firing off some custom code. The custom code does, well, whatever you want. After this, you clean up by deleting the special view from the template and redirecting back to the view.

Related

Magento limit admin products

I'm currently in the process of messing about with Magento, and I'm just wondering if anyone knows where I can modify the collection that is used for Mage_Adminhtml_Catalog_ProductController (app/code/core/Mage/Adminhtml/controllers/Catalog/ProductController.php)? Which method am I rewriting/creating to change the products to show?
EDIT1: I'd rather have a way of using observers, I.e. which event do I need to apply my code to?
EDIT2: best to add here what I'm trying to achieve; I've added an extra attribute to products which holds which admin added that specific product. Now In the admin panel, when a user views the list of products, they only see the products where the that attribute is their admin id.
EDIT3: I just stumbled upon catalog_product_load_after event observer, and I'm not sure if this is the right one, but this is what I have:
confix.xml
<?xml version="1.0"?>
<config>
<adminhtml>
<events>
<catalog_product_load_after> <!-- Name of Event -->
<observers>
<load_after> <!-- Any Unique Identifier -->
<type>singleton</type>
<class>Drench_Admindetails_Model_Observer</class> <!-- Over Model Class -->
<method>loadAfter</method> <!-- name of function -->
</load_after>
</observers>
</catalog_product_load_after>
</events>
</adminhtml>
</config>
and Model/Observer.php
<?php
class Drench_Admindetails_Model_Observer{
public function loadAfter(){
fb('testasd'); // this just a firephp call
}
}
You could listen to catalog_product_collection_load_before or catalog_product_collection_load_after.
To find the event you're interested in, use Alan Storm's technique: edit app/Mage.php dispatchEvent() method, adding this line at the beginning:
Mage::log('Event: ' . $name, null, 'events.log');
This will write all events in an /var/log/events.log file.
I'd put a link to Alan's original post, but I can't find it right now: if anyone find it I'll update my answer.
The quickest method is to copy this file
app/code/core/Mage/Adminhtml/Block/Catalog/Product/Grid.php
into your local scope and add new filter to _prepareCollection() function.
However this may not be upgrade-friendly. For an upgrade-firendly solution I suggest to create a new Magento module and append filter to _prepareCollection() function there.

SharePoint:FormField in a custom webpart?

On my home page, I want a simple webpart that allow users to quickly fill an entry in a list.
The list have, let's say, three fields : title (text), body (rich text), category (lookup).
I don't want to use the standard DataFormWebPart because I have a bit of code-behind that also fill some technical hidden fields of my list (actually, I don't exclude the DataFormWebPart, but I didn't find how to use it with code behind).
So I started to implement a custom webpart. Because I don't want to have to handle manually each field input, I started to use the FormField control, which automatically choose the rendering control, and provide a Value property with the correct format :
<SharePoint:FormField runat="server" id="fldTitle" FieldName="Title" />
This code is not sufficient, I have to specified the listid :
<SharePoint:FormField runat="server" id="fldTitle" FieldName="Title" ListId="{title list guid}" />
This is working quite correctly. I can in code access the fldTitle.Value to retrieve the user input.
BUT I have to include the webpart in a properly packaged and deployed feature, that can be activated. The webpart will always target the same list, but as the list is also instanciated in the feature (ListInstance element), I can't know the Guid in advance.
I've tried using several technics to set the list ID on the fly, but without success.
I've also "reflectored" the SP dlls to notice FormComponent class are using a "Context" that is set by ListFormWebPart.
Finally, my questions are :
is it the correct way to create a custom input webpart on the home page (not a list custom form) ?
how can I keep the behavior of the FormField (choose the right control and handle the input and its conversion to the storage format) ?
Will I have to create a custom ListFormWebPart ?
May I play with ControlTemplates ?
thanks in advance for the help... I'm struggling with this simple case for days now...
I think that customizing form templates is the easiest way to customize list forms. Since custom form templates are implemented as user controls you can add whatever code you want.
See the following article:
http://www.codeproject.com/KB/sharepoint/SharePointListForms.aspx

Feature element repeatedly added with every feature activation/deactivation

This is a very minor behavior when compared with the entire scope, but it is one that I'd like to put a stop to.
I have created a very, very simple SharePoint Feature. It has two elements in its manifest: an aspx webpart page, and an elements xml. I'll paraphrase my elements xml, which just adds a module, below.
<Elements xmlns="http://schemas.microsoft.com/sharepoint/">
<Module Name="Pass" Url="" Path="">
<File Url="pasq.aspx" NavBarHome="True" Type="Ghostable">
<AllUsersWebPart WebPartZoneID="Left" WebPartOrder="0">
<![CDATA[
WARGH THIS PART DOESN'T MATTER FOR THIS QUESTION!
]]>
</AllUsersWebPart>
</File>
</Module>
</Elements>
Now, on the first time I deploy and activate this feature, it works properly. But if I have to deactivate and then reactivate the feature in order to fix some properties in the webpart, then I find myself with a second webpart on the page. Naturally, each similar cycle will just add more and more. I understand that a new webpart must be created in order to employ the changes I just made, but why is the old webpart still on the page? Can I make this older webpart go away automatically as part of the feature activation/deactivation process without needing to employ a Receiver class?
EDIT
Based on the fact that I've already employed a Receiver to solve this issue, I ended up just adding the webpart removal as part of feature deactivation. But I was still hoping that maybe there's something I'm missing.
Unfortunately the content will be repeated unless you do one of the following :
Solution 1 : you can add a similar code to the activate feature to add webparts in the pages as follows:
SPFile file = page.File;
using (SPLimitedWebPartManager WebPartManager =
file.GetLimitedWebPartManager(PersonalizationScope.Shared))
{
try
{
SPLimitedWebPartCollection webparts = WebPartManager .WebParts;
if (webparts.Count == 0)
{
//set your web part
WebPartManager.AddWebPart(webpart, "MomoZone", 1);
}
}
catch(Exception ex)
{
throw;
}
finally
{
WebPartManager.Web.Dispose();
}
}
Solution 2 : you can use site definition with and Onet file that runs only once the site is created so you would have no more of this problem while setting your efatures to hidden
Cheers
SharePoint doesn't offer a declarative way (xml) to remove feature elements automagically. You'll have to add some code in a FeatureDeactiving method on the feature receiver to remove it programatically, sorry.

Sharepoint web parts Rendering html and controls

I have a webpart that displays HTML output within the RenderWebPart method and also creates controls within the CreateChildControls both methods are declared as overridden in the webpart.
My question is how to I control the order of the display of the controls and html output?
At the moment I call EnsureChildControls() within the RenderWebPart mthod to ensure all controls within the CreateChildControls are created and then the html out is rendered.
What if I wanted to display a control on the page then html output and then another control below in that order?
I would recommend moving all of your static HTML out of the Render function and into the CreateChildControls function. If you need to, you can add regular old HTML using Labels, WebControls, or even better... LiteralControls. Then you can just add them to your Controls collection.
Example:
WebControl container = new WebControl(System.Web.UI.HtmlTextWriterTag.Div);
StringBuilder sb = new StringBuilder();
sb.Append("<ul>");
foreach (Node child in this.Children)
{
sb.AppendFormat("<li>{1}</li>", url, name);
}
sb.Append("</ul>");
LiteralControl l = new LiteralControl();
l.Text = sb.ToString();
container.Controls.Add(l);
I'm actually relatively new to SharePoint and I was very astounded initially that one had to create individual controls manually, without a GUI interface like any normal ASP.NET web development involves. However, I had a very very good tip that allowed me to still use a GUI interface, and use it to create webparts for Sharepoint. It involves wrapping WebUserControls in webparts.
The link I used is here: http://www.a2zdotnet.com/View.aspx?id=95
You basically create WebUserControls (.ascx file) in a website, and so you can just add your controls like any normal .aspx page. You can also have a normal code-behind file (.ascx.cs). You then drag the .ascx file onto an .aspx file, so that it will then use your WebUserControl. When your .ascx files are ready and built, you copy them to the Layouts directory in the 12-Hive of your SharePoint Server. Best to create a sub-directory in there, to avoid clashing with other files in there already.
You then need to create a separate class Library project, that will have your WebPart code on. You then tell your WebPart to use your .ascx files in the layouts directory. Something like this:
protected override void CreateChildControls()
{
base.CreateChildControls();
try
{
this.Controls.Clear();
_myControl = this.Page.LoadControl("\\_layouts\\MyFolder\\WebUserControl.ascx");
this.Controls.Add(_myControl);
}
catch (Exception e)
{
err = e.Message;
}
The link I provided above provides more information, but basically you compile the webpart project and then add the DLL to the BIN directory of your sharepoint server (c:\inetpub\wwwroot\wss\ etc). You don't have to compile it to the GAC, by the way.
Then you add a entry to the web.config of your sharepoint server:
<SafeControl Assembly="MyWebUserControl, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9f4da00116c38ec5" Namespace="MyWebUserControl" TypeName="*" Safe="True" />
If you didn't compile the DLL to the GAC, but the BIN instead, you just need:
<SafeControl Assembly="UserControl" Namespace="UserControl" TypeName="*" Safe="True" />
Again, the link I posted above has it written in the code, a reference to the GUID, which is only needed if you placed the DLL in the GAC. You don't need the GUID part of the code if you only placed in the BIN directory.
Hope that helps.
Ash

Sharepoint item updating event - cancel event back to editform page?

I have an event receiver for a content type to validate some data in the ItemUpdating event. If I cancel the event (some of the data isn't valid for example), I set the properties cancel to true:
properties.Cancel = true;
properties.ErrorMessage = "...";
SharePoint cancels the updating event ok, but shows the standard SharePoint error page (with the specified message). Only problem is, I've got a complaint that this isn't actually very useful - we should return to the EditForm page so the details can be updated.
Has anyone done this, is there an easy way? The only suggestion I've had is that I can implement my own error page, but that's sounding quite a heavy solution to a (theoretically) simple process.
You could try to output HTML code (which includes javascript as well) in the ErrorMessage. BUT even if you do, the problem is that you have no safe way back to the data the user has entered. Either you make a HTTP/301 redirect and then it's a new page load, or you make the client go history.back() with JavaScript and then the browser may reload the page.
The official way of doing this is that you create a list definition and customize the list template. Then you edit the edit form template and include as many ASP.Net validator controls as needed. Then, implement the server side logic as you need. This article explains the technique: http://msdn.microsoft.com/en-us/library/aa543922.aspx
EDIT: To attach a custom control for editing of a specific contenttype, you add an XmlDocuments section to your ContentType definition. For instance, like this
<ContentType
..........
<XmlDocuments>
<XmlDocument NamespaceURI="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
<FormTemplates xmlns="http://schemas.microsoft.com/sharepoint/v3/contenttype/forms">
<Display>ContentTypeName_DispForm</Display>
<Edit>ContentTypeName_EditForm</Edit>
<New>ContentTypeName_NewForm</New>
</FormTemplates>
</XmlDocument>
</XmlDocuments>
.......
Then you create your own yoursolution_controltemplates.ascx file, which contains as well such blocks:"
<SharePoint:RenderingTemplate ID="ContentTypeName_DispForm" runat="server">
<Template>
<!-- put whatever controls you need here, we typically create a
separate custom control which implements everything-->
</Template>
</SharePoint:RenderingTemplate>
You can try to redirect using CopyUtil : http://weblogs.asp.net/jan/archive/2008/02/26/copyutil-aspx-a-little-sharepoint-gem.aspx
link = "http://yoursite/_layouts/CopyUtil.aspx?Use=id&Action=dispform&ItemId=X&ListId=X&WebId=X&SiteId=X";
Page.Response.Redirect(link)
maybe this will work

Resources