Main Page Sitemap

Most viewed

StrataSearch 3.01a Serial numbers included
Form Designer .NET 1.2 Available Greatis Software announced Form Designer .NET 1.2, a run-time form designer component for .NET versions of Visual Studio and Delphi. Form Designer .NET works at run time, as opposed to standard design-time form designers. Form Designer .NET replicates the...
Read more
iSpring Presenter 64-bit 8.0.0.11058 with Product keys
Please check your email once you paid, in order to see which payments description you can expect on your statement. Subscription Files will be deleted after 60 days since last download Q.What is a subscription and what is it for? A.A Premium subscription allows...
Read more
SectionMaker 2.0 Crack path + keygen
SEE THIS VIDEO - Click HERE for the full video (3MB Shockwave movie in a new window) WATT METERS FURTHER PROVE THE OVERUNITY ISSUE. A Center 232 clamp meter reading DC watts as Kilo Watts is inserted in the circuit on the input side...
Read more

SQL DB Validator 1.2 License Key Code


Jenwave Narrator 1 with serial number

sQL DB Validator 1.2 License Key Code
sQL DB Validator 1.2 License Key Code

Article

Browse Code

Stats

Revisions

Alternatives

Add your own
alternative version

Posted 9 Mar 2007

  • Download DocumentApplication_source.zip - 86.7 KB

Screenshot - wwf_document.gif

The purpose of the article

Demonstrate, a quick view of workflow foundation, and highlight the basic knowledge that

we should know before create first useful workflow application,

and highlight a design guide lines to create reusable workflow applications.

Finally, to taste the workflow through a sample project that is generic and useful for any

document workflow system, from the client application to administration application using

database.

Workflow foundation, in brief:

We can see the workflow foundation, simply as a collection of activities that can work

together through conditions and rules for specific purpose.

Workflow foundation is not if else blocks with loop blocks, and if it was implemented as

this simple algorithm, it will cost us a lot for no benefits. The bottom line of using

workflow foundation is, simplifying a

complex algorithm to simple visual editable, maintainable workflow,

Replaced my old design, with workflow:

I used the command patterns a lot in my work, as an encapsulation of the request in

single object called command, that is initialized with parameters,

then execute method will be invoked to retrieve the result, or to do the specific tasks,

But I always have a problem in sharing the properties, information and resources, or the

result(s) of the other commands,

Some commands were waiting until other commands finished,and others will begins from

the ending point of others,

To solve this problem I always use a mediator which in most cases is CommandManager

class that has a collection of a commands, and knows the order of execution of all

commands,and share all parameters, through the container scope, and each command has

a reference to the command manager,

so it can access all the properties of all other command very easy, or setting any

property for other command or the out client application.

Finally

to make the design good, I always inherit all the commands from CommandBase

abstracted class to give me the contract of all commands,so the CommandManager can

execute and initialize all childs commands.

The funny thing is,

that each time I try to create the command objects, I found myself made the same

generic pattern,and wondering if there any way to visualize these library for better use?

Once I saw the sequential workflow activities first time, I said "Aha"

That is exactly what I want, commands (Activities ), has a container command manager

(SequentialWorkflowActivity),and all commands has the same interface or base class

(Activity), and all commands can get the data through contacting the command manager

Sequential workflow

Finally visualizing every thing for quick development, and for better maintainability it is a

great stuff.

What impressed me in workflow foundation?

Sure every thing in workflow foundation impressed me, But the Idea of Adding service(s)

in runtime, then using this service in the workflow through referencing the service

using the public method GetService of WorkflowRuntime object, or through special

activities like CallExternalMethodActivity, HandleExternalEventActivity, it is

fantastic Idea,

because simply you can separate all the abstracted classes and interfaces to a

separate assembly, then make the workflow and the core application reference that

assembly, so you increased the level of reusability of workflow, because the workflow was

coded against the interface(s) but you can in runtime register any concrete class, that

implement this interface,

you can integrate the wonderful Provider pattern that introduced in.Net 2 with the

wonderful workflow foundation through registering any service in web.config, in workflow

section (this topic is beyond this simple project) Once you understand how to use, the

custom service with in the workflow You will understand all

other services, including "out of box" services like WorkflowPersistenceService,

WorkflowSchedulerService, TrackingService,..

Basics that you should know,

Before developing, the first practical application,

as I mentioned, using workflow as a bunch of "if else" statements, is not good idea at

all, and you should know the minimum information about workflow You should know

• How to pass parameters to workflow and to retrieve parameters, after the workflow

completed.

• How to create a custom activity, to work for specific task.

• How to use custom service, and consuming this service inside the workflow.

• How to pass data from host application to workflow or from workflow to application

through workflow processing. I think that is the minimum that you should know to create

a useful workflow application For advanced work with the workflow you may need to now,

• Using WorkflowPersistenceService, and Load and Unload to database,

• Using Transaction scope for data consistency.

• Create fault handler, in case of errors happened inside the workflow. Sue that is a lot to

mention about, the workflow specially if you, use the workflow in Asp.NET applications or

in webservice,

Or you think to update simple windows form after completing the workflow

or in specific events. But that is not the purpose of this article.

Design guide lines to work with workflow foundation

I tried to apply the workflow with objects that I already wrote before using my skills In

object oriented programming, using the workflow but I found it is quite difficult to use the

workflow, without any preparation to my code, exactly like first time I tried to use Unit

test to test "not testable " applications

I found that I have to make my code testable through dependency injection and

constructor injection and mockobjects,

Now To use the workflow you have to change your way in developing, So instead using

100% object oriented programming you should move object behaviors from concrete

classes to services,

so your code will be more service oriented

Steps for preparing your code, before using workflow:

Basic 3 steps

1) Separate all object behaviors to Services.

2) Extract the Interface for these services (user refactoring in voisual stodio 5), so all

your services should implement services interfaces

3) Separate these interfaces to separate assembly

Consider this simple example

Assume you always use a class called

Customer, has these properties

ID, Name, Address,

and has these methods

Add(), Update(), Delete(), Attach(), Register(), Unregister(),..

To use the workflow, you should separate this object state and Identitylike like ID, Name,

Address And move the behavior (public methods, events,..) to CustomerService that

Implement ICustomerService.

In this case you can use the workflow very easy with your prepared code.

In source sample,

you will see the Document Object is inherited from the IDocumentService, and both of

them used for describing the whole document process

The Sample project in Brief

The Solution sample is 3 basic projects and other 2 projects for common objects and

interfaces.

The Idea of the Application is

The user will create a document with his client application,

the user still can update his document as long as is in Open sate.

Then the user will kick the document to workflow, then depending on the document

properties, the workflow will choose the Administrator (s), that is delegated to approve

this document, and will save the data to database and will wait until the Administrator to

complete the cycle.

The Administrator after the login screen (sample will work without password) will see all

documents that are waiting for him,

and then he will approve or Reject the document,

so the document will be flagged as approved or to open state again to the user that can

update the document or delete it or to re submit again to the workflow,

Client Application Sample

Create document inside the main form

private void btnCreateDocument_Click(object sender, EventArgs e) { if (ValidateForm()) { Document _document = new Document(); _document.Category = (Category)this.cmbCategory.SelectedItem; _document.Title = this.txtTitle.Text.Trim(); _document.Description = this.txtDescription.Text.Trim(); _document.Depositor = User.CurrentUser.UserName; _service.CreateDocument(_document); ConfirmSuccess(); } else { MessageBox.Show("Please complete, the form's fields", "Validation, Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } }
IDocument services class diagram

document Database Diagram

Document services class diagram

[Serializable] public class DocumentService : IDocumentService { public Document GetDocumentByID(int id) { return DocumentDalc.GetDocumentByID(id); } public Documents GetCreatedDocuments(string sQL DB Validator 1.2 License Key Code depositor, Status status) { return DocumentDalc.GetAllCreatedDocuments(depositor, status); } public Documents GetCreatedDocuments(string depositor) { return DocumentDalc.GetAllCreatedDocuments(depositor); } public Documents GetAllNeedToApproveDocuments(string userName) { return DocumentDalc.GetAllNeedToApproveDocuments(userName); } public DocumentWorkflowItem GetWorkflowItem(Document document) { DocumentWorkflowItem wfItem = new DocumentWorkflowItem(document); return wfItem; } #region IDocumentService Members public void CreateDocument(Document document) { DocumentDalc.CreateDocumentRecord(document); if (this.Created!= null) Created.Invoke(this, new DocumentEventArguments(document)); } public void UpdateDocument(Document document) { DocumentDalc.UpdateDocumentRecord(document); if (this.Updated!= null) Updated.Invoke(this, new DocumentEventArguments(document)); } public void ArchiveDocument(Document document) { document.Status = Status.Archived; DocumentDalc.UpdateDocumentRecord(document); if (this.Archived!= null) Archived.Invoke(this, new DocumentEventArguments(document)); } public void DeleteDocument(Document document) { document.Status = Status.Deleted; DocumentDalc.UpdateDocumentRecord(document); if (this.Deleted!= null) Deleted.Invoke(this, new DocumentEventArguments(document)); } public void CreateInWorkflowDocumentForUser(Document document, string approvalUser, Guid workflowID) { DocumentDalc.CreateInWorkflowDocumentRecordForUser(document, workflowID, approvalUser); if (this.SentToWorkflow!= null) SentToWorkflow.Invoke(this, new DocumentEventArguments(document)); } public void CreateInWorkflowDocumentForGroup(Document document, string approvalGroup, Guid workflowID) { DocumentDalc.CreateInWorkflowDocumentRecordForGroup(document, workflowID, approvalGroup); if (this.SentToWorkflow!= null) SentToWorkflow.Invoke(this, new DocumentEventArguments(document)); } public void ApproveDocumentWorkflow(Document document, string approvalUser, Guid workflowID) { DocumentDalc.CreateApprovedDocumentRecordForUser(document, workflowID, approvalUser); if (this.Approved!= null) Approved.Invoke(this, new DocumentEventArguments(document)); } public void RejectDocumentWorkflow(Document document, string approvalUser, Guid workflowID) { DocumentDalc.CreateRejectedDocumentRecordForUser(document, workflowID, approvalUser); if (this.Rejected!= null) Rejected.Invoke(this, new DocumentEventArguments(document)); } #endregion #region IDocumentService Members public event EventHandler<externalexternaldocumenteventargument __designer:dtid="1688849860263990" /> RaiseApproveDocumentWorkflowEvent; public event EventHandler<externalexternaldocumenteventargument /> RaiseRejectDocumentWorkflowEvent; #endregion public void RaiseApproveDocumentWorkflow(DocumentWorkflowItem wfItem) { ExternalExternalDocumentEventArgument arg = new ExternalExternalDocumentEventArgument(wfItem.WorkflowID, wfItem.Document, User.CurrentUser); if (RaiseApproveDocumentWorkflowEvent!= null) { RaiseApproveDocumentWorkflowEvent.Invoke(this, arg); } } public void RaiseRejectDocumentWorkflow(DocumentWorkflowItem wfItem) { ExternalExternalDocumentEventArgument arg = new ExternalExternalDocumentEventArgument(wfItem.WorkflowID, wfItem.Document, User.CurrentUser); if (this.RaiseRejectDocumentWorkflowEvent!= null) { RaiseRejectDocumentWorkflowEvent.Invoke(this, arg); } } #region IDocumentService Members public event EventHandler<documenteventarguments /> Created; public event EventHandler<documenteventarguments /> Updated; public event EventHandler<documenteventarguments /> Archived; public event EventHandler<documenteventarguments /> Deleted; public event EventHandler<documenteventarguments /> SentToWorkflow; public event EventHandler<documenteventarguments /> Approved; public event EventHandler<documenteventarguments /> Rejected; #endregion }
Administration Application Program


private void approveToolStripMenuItem_Click(object sender, EventArgs e) { _documentID = int.Parse(dgrdDocuments.CurrentRow.Cells["ID"].Value.ToString()); DialogResult result = MessageBox.Show("Are you sure that you want to Approve this document", "confirmation", MessageBoxButtons.YesNo, MessageBoxIcon.Question); Document doc = null; if (result == DialogResult.Yes) { doc = _service.GetDocumentByID(this._documentID); if (doc.Status!= DocumentBaseLibrary.Status.InWorkflow) { MessageBox.Show(string.Format("This document {0} is not in Workflow state", doc), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return; } Approve(doc); } } private void Approve(Document doc) { DocumentWorkflowItem wfItem=_service.GetWorkflowItem(doc); // Get the type of the workflow Type type = typeof(DocumentWorkflow.DocumentWorkflow); // Start the workflow instance Guid id = wfItem.WorkflowID; try { WorkflowInstance inst = theWorkflowRuntime.GetWorkflow(id); inst.Load(); this._service.RaiseApproveDocumentWorkflow(wfItem); } catch(Exception ex) { MessageBox.Show("The Document cannot be loaded. " + ex.ToString(), "Administration", MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } }

Conclusion

Workflow foundation, it take off completely the burden of all complexity of the

Application rules and decisions

But, to achieve the maximum reusability, you have to

prepare your code by creating services, and separate abstracted services in separate

assemblies

License

Share


Enhanced Document workflow approval v2

Microsoft Guide to Modern Dev/Test

SharePoint Quick Start FAQ Part 6 – Workflows, Workflows and Workflows

How-To Intel® IoT Technology Code Samples: Alarm clock in C++

SharePoint Workflow Basics

How-To Intel® IoT Technology Code Samples: Access control in C++

Comments and Discussions

Hi Rafkey, its nice article to start with. I have a one question though...
My Workflows are not persisted in Workflow instance table in "workflowdb".
if you can elaborate what am i missing? I am running your source code.I have run all the scripts successfully, each and every table and SP are present in the "workflowdb".
Needs a password? I dont have the workflowDB even in the script area that you save in the zip file
An unhandled exception of type 'System.ArgumentException' occurred in System.Workflow.Runtime.dll
in line 58 frmMain.cs
if you are using a worflowDb it will be better in a workflow mannager db??
http://msdn.microsoft.com/en-us/library/dn217859(v=azure.10).aspx[
Is it possible to implement same document approval workflow system in Asp.Net mvc4 razor application without using wwf or wcf (with the help of model classes, view and controller classes only). If yes, How we can implement it?
Thanks for reading my query,

Tim


when i run your app in visual stuio it gives error
A network-related or instance-specific error occurred while establishing a connection to SQL Server. The server was not found or was not accessible. Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (provider: Named Pipes Provider, error: 40 - Could not open a connection to SQL Server)
in this code
adapter.Fill(ds, "tableData");
remember to run database scripts in folder Database of the zip source code.

patel


how to design workflows in asp.net?
how to start
thankxConfused | :confused:
Can u please let me know the password for the users, which we select from the dropdown at login page.
u don't need password just fill txt of username and click login
note that u must fill username found in tbll_users of documentDB database
Omnia Ahmed
senior software engineer
It's a fantastic example.
Dear Refky Wahib
One of the developers comment on your project goes like this
Please, don't assume your readers are all experts in the topic you are writing in.
Could you please illustrate all steps required to run the sample, especially those related to the database?
Thanks in advance.
I am also saying the same thing.
Please do not think or assume that all readers are rated programmers.
What you people submitting code must do is think of beginners.
Please write it in such a way that all people could follow what you write.
First Requirements. Then Details of Database creation. How to start the project by expaining the steps for creating this project. Then How this works.
People will get familiar with the new subject only By Creating or reproducing the same code by him/herself, Debugging, changing to his needs.
Please help.
I think this piece of code is a good start. But Not even the Dowloaded project is working.
So Please help us. Actually I was hunting for a good working sample of WWF. This made me so dissapointing.
Could you please tell us how to get it working.
Or Could you Write another project for the Task Given below.
I need to enter my leave application. Then It has to pass thru Project Manager. After Pms Approval It has to Pass to HR. If PM Rejects I get a mail. Again same for Hr Also. If the two Approve it then a mail a should be send to Employee and Pm and HR.
Please help by creating project. and send it for miilions to read with all explanation.
This project may be more than enough for 10000000000000 other samples.
Sen K. Mathew
Hi
What you need to do is create an empty database called documentDB and workflowDB. Then run the.sql scripts in the following order (on the documentDB database):
_tbls.sql ; _stps.sql and _views.sql
You will also need to change the two app.config files in the project make sure they reference your instance of SQL Server. I think this is a phenomenal example and hope you figure it out.
Cheers
SLS
when i create database i.e document db and select new query and exeute this
_tbls.sql ; _stps.sql and _views.sql
then following error is coming
Incorrect syntax near '_stps'.
Hi Refke,
Can you please tell me where is the workflow DB? There are 4 query files by name
SqlPersistenceService_Logic
SqlPersistenceService_Schema
Tracking_Logic
Tracking_Schema
in the windows/framework/v3.0/sql/en...
I created a fresh db by name workflow db and ran the above 4 script files. It is giving me error saying the user name "statepersistent..." doesnot exist when I run the 3 script and
the username "track.." doesnot exist when I run the 1st script.
Can you please tell me how to get rid of this error and how to set up the workflow db?
Thanks,
djad
Hi,
i have created a document with the user tool.
The document was created successfully, i can see it in 'view in workflow'.
Status 'InWorkflow', category 'purchase'
Now i change to the admin prg.
The list is empty.
Does the workflow works?
Thanks for help.
Your application is a good demo for working with wf.
i met the same error.can everyone answer?
hi
why ur application geting this error
in the line 61 frmMain -> wr.StartRuntime();
"Could not find stored procedure 'RetrieveNonblockingInstanceStateIds'."

............................................................................


You need to point to the correct persistence DB name.
In my case, I just ran the script under:
C:\WINDOWS\Microsoft.NET\Framework\v3.0\Windows Workflow Foundation\SQL\EN
And the following is the connection string I gave:
connectionString="Data Source=(local);Initial Catalog=WorkflowPersistence;Trusted_Connection=true"
providerName="System.Data.SqlClient" />
Hope it helps.

I have been working in the field of Microsoft technologies for around 8 years. Have worked right from classic ASP to VB, VC++ ATL, C#, ASP.NET, BizTalk, Sharepoint. Curretnly working as a.NET consultant @ Chicago.


Hi Refky,
Thanks for the solution provided here. I'm to execute the code and I running into an issue with the stored procedure mentioned above. I created a DB called WorkflowPersistence and apllied all the sql in the "C:\Windows\Microsoft.NET\Framework\v3.0\Windows Workflow Foundation\SQL\en" folder. I can see that the procedure is created in the DB. However, I don't know where to change the code to make this changes: " connectionString="Data Source=(local);Initial Catalog=WorkflowPersistence;Trusted_Connection=true" providerName="System.Data.SqlClient" /> "
Can you help a bit?
Thanks,
Pierre
Please, don't assume your readers are all experts in the topic you are writing in.
Could you please illustrate all steps required to run the sample, especially those related to the database?
Thanks in advance.
If I create a document, submit it ot the workflow, reject it, re-submit it and try to approve, I get this exception: "Workflow entries, should =1"
This is because all records for this document id with status "InWorkflow" are returned; including the one which was already rejected.
To overcome this behaviour, USP_Select_Document_Workflow could be modified so it returns only one record:
ALTER PROCEDURE [dbo].[USP_Select_Document_Workflow]
(
@document_id int =NULL
)
AS
SET ROWCOUNT 1
SELECT transaction_id, document_id, document_approval_user, document_is_group, document_workflow_id, document_status, last_update_date
FROM tbl_document_workflow
WHERE (document_status = N'InWorkflow') AND (document_id = @document_id)
ORDER BY transaction_id DESC
SET ROWCOUNT 0
Besides this, your article is a good reference to start exploring workflow persistence.
- Bart
Rusty on db creation from.sql and.txt files? Well, created the documentDB within Visual Studio Administration project as documentDB.mdf.
Then double clicked and ran document_.sql
However; executing document_stps.sql throws:Cry | :((
Msg 207, Level 16, State 1, Procedure USP_Inser_Document, Line 23
Invalid column name 'document_category'.
Msg 207, Level 16, State 1, Procedure USP_Inser_Document, Line 44
Invalid column name 'document_category'.
Msg 207, Level 16, State 1, Procedure USP_SELECT_LIST_tbl_document, Line 6
Invalid column name 'document_category'.
Msg 207, Level 16, State 1, Procedure USP_Select_ALL_Document_Workflow_For_User, Line 14
Invalid column name 'document_category'.
Msg 207, Level 16, State 1, Procedure USP_Select_ALL_Document_Workflow_For_User, Line 25
Invalid column name 'document_category'.
Msg 207, Level 16, State 1, Procedure USP_Select_Document_By_Deposetor_Status, Line 10
Invalid column name 'document_category'.
Msg 207, Level 16, State 1, Procedure USP_Select_Document_By_ID, Line 12
Invalid column name 'document_category'.
Msg 207, Level 16, State 1, Procedure USP_Select_InWorkflow_Document_Workflow_For_User, Line 13
Invalid column name 'document_category'.
Msg 207, Level 16, State 1, Procedure USP_Select_InWorkflow_Document_Workflow_For_User, Line 24
Invalid column name 'document_category'.
Msg 207, Level 16, State 1, Procedure USP_Update_Document, Line 28
Invalid column name 'document_category'.

ASP/XML/VB/Com Developer turned ASP/ADO/.Net'n c# Enthusiast
Dhahran,Saudi Arabia


Ok, renamed document_cate... to document_category and got past the 3 scripts.
WorkflowDB: Running the.SQL C:\WINDOWS\Microsoft.NET\Framework\v3.0\Windows Workflow Foundation\SQL\EN files against a clean workflowDB...Confused | :confused:

ASP/XML/VB/Com Developer turned ASP/ADO/.Net'n c# Enthusiast
Dhahran,Saudi Arabia


you should run the creating tables script, before the stored procedures scripts Smile | :)Smile | :)
Run document_tbls.sql
before
Run document_stps.sql
I did not mention it, because it is very clear,
if someone else has any issues about creating db
I will modify the article and will add this section.
by the way
the.txt is not sql script
it is sample of data, you can put it manually.
or create your own data, or just copy and paste in any datagrid tools

Refky Wahib
senior programmer, center for learning and innovation


When i click on login page after executing all the scripts..
It is giving me following error
Could not find stored procedure 'RetrieveNonblockingInstanceStateIds'.
There is no procedure named RetrieveNonblockingInstanceStateIds present in your script.

Manish M Meshram


I give this error when compile solution:
'System.Reflection.TargetInvocationException' while invoking the validator
'System.Workflow.ComponentModel.Compiler.CompositeActivityValidator'
on activity 'DocumentWorkflow':
Exception has been thrown by the target of an invocation.
C:\Isidro\Programacion\Code\DocumentApplicationWF_source\DocumentApplication_source\DocumentWorkflow
Please help me.
Geovanny Santana
Software Developer
Dear Geovanny
I did not see this specific error, in this workflow application sample
but I have some thoughts about similer error,
for me it looks like, incompatibility of versions, in.Net library made this error
first you should check if you can run any other samples or not?
you should open different projects from the samples that introduced with Microsoft http://msdn2.microsoft.com/en-us/library/ms741723.aspx
because may be your current version is not stable, or not properly configured
or not properly upgraded
I faced few troubles because I upgraded the.Net3 from B to release, but did not upgrade the visual studio workflow extension release as well,, so I will give you my to environment and you see if you have the same environment
1) you should have.Net3 release candidate be installed in your computer
http://www.microsoft.com/downloads/details.aspx?familyid=10CC340B-F857-4A14-83F5-25634C3BF043&displaylang=en
2) be sure that you use the right version of Visual studio extension for windows workflow
http://www.microsoft.com/downloads/details.aspx?familyid=e8232f93-48f0-4e74-b09d-b51f1d4231a4&displaylang=en
and you should see the workflow designer without any error
3) I used SQL Server 2000
so check these things as first step
good luck

Refky Wahib
senior programmer, center for learning and innovation


Thanks. No errors.
Where is the workflowDB?
C:\WINDOWS\Microsoft.NET\Framework\v3.0\Windows Workflow Foundation\SQL\EN
yes
good luck

Refky Wahib
senior programmer, center for learning and innovation


what is the name of the tool used to make workflow diagram?
Windows Workflow Foundation, I believe.
All Class Diagrams, was through visual stodio 2005
for workflow, you shold install Microsoft® Visual Studio® 2005 Extensions
http://www.microsoft.com/downloads/details.aspx?familyid=e8232f93-48f0-4e74-b09d-b51f1d4231a4&displaylang=en[^]

Refky Wahib
senior programmer, center for learning and innovation


General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   


102
Sitemap