diff --git a/SHANDEMVAIO.testrunconfig b/SHANDEMVAIO.testrunconfig new file mode 100644 index 0000000000..48adf6cd4e --- /dev/null +++ b/SHANDEMVAIO.testrunconfig @@ -0,0 +1,10 @@ + + + This is a default test run configuration for a local test run. + + + + + + + \ No newline at end of file diff --git a/umbraco.Test/AuthoringTests.txt b/umbraco.Test/AuthoringTests.txt new file mode 100644 index 0000000000..64bab9481d --- /dev/null +++ b/umbraco.Test/AuthoringTests.txt @@ -0,0 +1,136 @@ +========================================================================== + Visual Studio Team System: Overview of Authoring and Running Tests +========================================================================== + +This overview describes the features for authoring and running tests in +Visual Studio Team System and Visual Studio Team Edition for Software Testers. + +Opening Tests +------------- +To open a test, open a test project or a test metadata file (a file with +extension .vsmdi) that contains the definition of the test. You can find +test projects and metadata files in Solution Explorer. + +Viewing Tests +------------- +To see which tests are available to you, open the Test View window. Or, +if you have installed Team Edition for Software Testers, you can also open +the Test List Editor window to view tests. + +To open the Test View window, click the Test menu, point to Windows, and +then click Test View. To open the Test List Editor window (if you have +installed Team Edition for Software Testers), click Test, point to Windows, +and then click Test List Editor. + +Running Tests +------------- +You can run tests from the Test View window and the Test List Editor window. +See Viewing Tests to learn how to open these windows. To run one or more +tests displayed in the Test View window, first select the tests in that +window; to select multiple tests, hold either the Shift or CTRL key while +clicking tests. Then click the Run Tests button in the Test View window +toolbar. + +If you have installed Visual Studio Team Edition for Software Testers, you can +also use the Test List Editor window to run tests. To run tests in Test List Editor, +select the check box next to each test that you want to run. Then click the +Run Tests button in the Test List Editor window toolbar. + +Viewing Test Results +-------------------- +When you run a test or a series of tests, the results of the test run will be +shown in the Test Results window. Each individual test in the run is shown on +a separate line so that you can see its status. The window contains an +embedded status bar in the top half of the window that provides you with +summary details of the complete test run. + +To see more detailed results for a particular test result, double-click it in +the Test Results window. This opens a window that provides more information +about the particular test result, such as any specific error messages returned +by the test. + +Changing the way that tests are run +----------------------------------- +Each time you run one or more tests, a collection of settings is used to +determine how those tests are run. These settings are contained in a “test +run configuration” file. + +Here is a partial list of the changes you can make with a test run +configuration file: + + - Change the naming scheme for each test run. + - Change the test controller that the tests are run on so that you can run + tests remotely. + - Gather code coverage data for the code being tested so that you can see + which lines of code are covered by your tests. + - Enable and disable test deployment. + - Specify additional files to deploy before tests are run. + - Select a different host, ASP.NET, for running ASP.NET unit tests. + - Select a different host, the smart device test host, for running smart device unit tests. + - Set various properties for the test agents that run your tests. + - Run custom scripts at the start and end of each test run so that you can + set up the test environment exactly as required each time tests are run. + - Set time limits for tests and test runs. + - Set the browser mix and the number of times to repeat Web tests in the + test run. + +By default, a test run configuration file is created whenever you create a +new test project. You make changes to this file by double-clicking it in +Solution Explorer and then changing its settings. (Test run configuration +files have the extension .testrunconfig.) + +A solution can contain multiple test run configuration files. Only one of +those files, known as the “Active” test run configuration file, is used to +determine the settings that are currently used for test runs. You select +the active test run configuration by clicking Select Active Test Run +Configuration on the Test menu. + +------------------------------------------------------------------------------- + +Test Types +---------- +Using Visual Studio Team Edition for Software Testers, you can create a number +of different test types: + +Unit test: Use a unit test to create a programmatic test in C++, Visual C# or +Visual Basic that exercises source code. A unit test calls the methods of a +class, passing suitable parameters, and verifies that the returned value is +what you expect. +There are three specialized variants of unit tests: + - Data-driven unit tests are created when you configure a unit test to be + called repeatedly for each row of a data source. The data from each row + is used by the unit test as input data. + - ASP.NET unit tests are unit tests that exercise code in an ASP.NET Web + application. + - Smart device unit tests are unit tests that are deployed to a smart device + or emulator and then executed by the smart device test host. + +Web Test: Web tests consist of an ordered series of HTTP requests that you +record in a browser session using Microsoft Internet Explorer. You can have +the test report specific details about the pages or sites it requests, such +as whether a particular page contains a specified string. + +Load Test: You use a load test to encapsulate non-manual tests, such as +unit, Web, and generic tests, and then run them simultaneously by using +virtual users. Running these tests under load generates test results, +including performance and other counters, in tables and in graphs. + +Generic test: A generic test is an existing program wrapped to function as a +test in Visual Studio. The following are examples of tests or programs that +you can turn into generic tests: + - An existing test that uses process exit codes to communicate whether the + test passed or failed. 0 indicates passing and any other value indicates + a failure. + - A general program to obtain specific functionality during a test scenario. + - A test or program that uses a special XML file (called a “summary results + file”), to communicate detailed results. + +Manual test: The manual test type is used when the test tasks are to be +completed by a test engineer as opposed to an automated script. + +Ordered test: Use an ordered test to execute a set of tests in an order you +specify. + +------------------------------------------------------------------------------- + + diff --git a/umbraco.Test/DocumentTest.cs b/umbraco.Test/DocumentTest.cs new file mode 100644 index 0000000000..b2eef67c39 --- /dev/null +++ b/umbraco.Test/DocumentTest.cs @@ -0,0 +1,837 @@ +using umbraco.cms.businesslogic.web; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using umbraco.BusinessLogic; +using umbraco.cms.businesslogic; +using System.Collections.Generic; +using System.Xml; +using Microsoft.VisualStudio.TestTools.UnitTesting.Web; +using System.Linq; +using System.Threading; +using umbraco.cms.businesslogic.datatype; +using umbraco.editorControls.textfield; +using umbraco.cms.businesslogic.propertytype; +using umbraco.cms.businesslogic.property; + +namespace umbraco.Test +{ + + + /// + /// Test for the Document API. + /// + /// + /// All of these tests are run in the ASP.Net context and these tests will require that there is data + /// in the Umbraco instance being tested including both document types and content. + /// + /// This WILL make alot of SQL calls. + /// + /// All of these tests will also delete any data that they create + /// + [TestClass()] + public class DocumentTest + { + + #region Unit Tests + /// + ///A test for making a new document and deleting it which actuall first moves it to the recycle bin + ///and then deletes it. + /// + [TestMethod()] + public void MakeNewTest() + { + //System.Diagnostics.Debugger.Break(); + string Name = "TEST-" + Guid.NewGuid().ToString("N"); + DocumentType dct = new DocumentType(GetExistingDocTypeId()); + int ParentId = -1; + Document actual = Document.MakeNew(Name, dct, m_User, ParentId); + var id = actual.Id; + Assert.IsTrue(actual.Id > 0); + RecycleAndDelete(actual); + } + + /// + /// A test for Copying a node, then deleting the copied node. + /// This does error checking on the case of when a node is being copied that is in the root and doesn't have a parent node, it will + /// lookup the root docs to do the test. + /// + [TestMethod()] + public void CopyTest() + { + //System.Diagnostics.Debugger.Break(); + Document target = new Document(GetExistingNodeId()); + int parentId = target.Level == 1 ? -1 : target.Parent.Id; + bool RelateToOrignal = false; + + //get children ids for the current parent + var childrenIds = target.Level == 1 ? Document.GetRootDocuments().ToList().Select(x => x.Id) : target.Parent.Children.ToList().Select(x => x.Id); + + //copy the node + target.Copy(parentId, m_User, RelateToOrignal); + + //test that the child id count + 1 is equal to the total child count + Assert.AreEqual(childrenIds.Count() + 1, target.Level == 1 ? Document.GetRootDocuments().Count() : target.Parent.ChildCount); + + //get the list of new child ids from the parent + var newChildIds = target.Level == 1 ? Document.GetRootDocuments().ToList().Select(x => x.Id) : target.Parent.Children.ToList().Select(x => x.Id); + //get the children difference which should be the new node + var diff = newChildIds.Except(childrenIds); + + Assert.AreEqual(1, diff.Count()); + + Document newDoc = new Document(diff.First()); + + RecycleAndDelete(newDoc); + } + + /// + /// Tests copying by relating nodes, then deleting + /// + [TestMethod()] + public void CopyAndRelateTest() + { + //System.Diagnostics.Debugger.Break(); + Document target = new Document(GetExistingNodeId()); + int parentId = target.Parent.Id; + bool RelateToOrignal = true; + + //get children ids + var childrenIds = target.Parent.Children.ToList().Select(x => x.Id); + + target.Copy(parentId, m_User, RelateToOrignal); + + Assert.AreEqual(childrenIds.Count() + 1, target.Parent.ChildCount); + + Document parent = new Document(parentId); + //get the children difference which should be the new node + var diff = parent.Children.ToList().Select(x => x.Id).Except(childrenIds); + + Assert.AreEqual(1, diff.Count()); + + Document newDoc = new Document(diff.First()); + + RecycleAndDelete(newDoc); + } + + /// + ///Create a new document, create preview xml for it, then delete it + /// + [TestMethod()] + public void ToPreviewXmlTest() + { + //System.Diagnostics.Debugger.Break(); + string Name = "TEST-" + Guid.NewGuid().ToString("N"); + DocumentType dct = new DocumentType(GetExistingDocTypeId()); + int ParentId = -1; + Document actual = Document.MakeNew(Name, dct, m_User, ParentId); + var id = actual.Id; + Assert.IsTrue(actual.Id > 0); + + XmlDocument xd = new XmlDocument(); + var xmlNode = actual.ToPreviewXml(xd); + + Assert.IsNotNull(xmlNode); + Assert.IsTrue(xmlNode.HasChildNodes); + + RecycleAndDelete(actual); + } + + /// + /// Run test to create a node, publish it and delete it. This will test the versioning too. + /// + [TestMethod()] + public void MakeNewAndPublishTest() + { + //System.Diagnostics.Debugger.Break(); + string Name = "TEST-" + Guid.NewGuid().ToString("N"); + DocumentType dct = new DocumentType(GetExistingDocTypeId()); + int ParentId = -1; + Document actual = Document.MakeNew(Name, dct, m_User, ParentId); + var id = actual.Id; + Assert.IsTrue(actual.Id > 0); + + var versionCount = actual.GetVersions().Count(); + + actual.Publish(m_User); + + Assert.IsTrue(actual.Published); + Assert.AreEqual(versionCount + 1, actual.GetVersions().Count()); + + RecycleAndDelete(actual); + } + + /// + ///A test that creates a new document, publishes it, unpublishes it and finally deletes it + /// + [TestMethod()] + public void PublishThenUnPublishTest() + { + //System.Diagnostics.Debugger.Break(); + string Name = "TEST-" + Guid.NewGuid().ToString("N"); + DocumentType dct = new DocumentType(GetExistingDocTypeId()); + int ParentId = -1; + Document actual = Document.MakeNew(Name, dct, m_User, ParentId); + var id = actual.Id; + Assert.IsTrue(actual.Id > 0); + + var versionCount = actual.GetVersions().Count(); + + actual.Publish(m_User); + + Assert.IsTrue(actual.Published); + Assert.AreEqual(versionCount + 1, actual.GetVersions().Count()); + + actual.UnPublish(); + + Assert.IsFalse(actual.Published); + + RecycleAndDelete(actual); + } + + /// + ///A test that makes a new document, updates some properties, saves and publishes the document, then rolls the document back and finally deletes it. + /// + [TestMethod()] + public void SaveAndPublishThenRollBackTest() + { + //System.Diagnostics.Debugger.Break(); + + //create new document in the root + string Name = "TEST-" + Guid.NewGuid().ToString("N"); + DocumentType dct = new DocumentType(GetExistingDocTypeId()); + Document actual = Document.MakeNew(Name, dct, m_User, -1); + var id = actual.Id; + Assert.IsTrue(actual.Id > 0); + + //get a text property + var prop = GetTextFieldProperty(dct, actual); + var originalPropVal = prop.Value; + var versionCount = actual.GetVersions().Count(); + + //save + actual.Save(); + Assert.IsTrue(actual.HasPendingChanges()); + + //publish and create new version + actual.Publish(m_User); + Assert.IsTrue(actual.Published); + var versions = actual.GetVersions().ToList(); + Assert.AreEqual(versionCount + 1, versions.Count()); + + prop.Value = "updated!"; //udpate the prop + Assert.AreNotEqual(originalPropVal, prop.Value); + + //rollback to first version + actual.RollBack(versions.OrderBy(x => x.Date).Last().Version, m_User); + + var rolledBack = new Document(id); + + Assert.AreEqual(originalPropVal, rolledBack.GenericProperties.ToList().Where(x => x.PropertyType.Alias == "headerText").First().Value); + + RecycleAndDelete(actual); + } + + /// + ///Tests creating a new document type, then creating a new node with that document type, adding some data to it, then deleting the + ///document type which should delete all documents associated with that. + ///This will create a document type that has it's own id allowed as children. When we create the content nodes, we'll create + ///them as children of each other to ensure the deletion occurs correctly. + /// + [TestMethod()] + public void DeleteAllDocsByDocumentTypeTest() + { + //System.Diagnostics.Debugger.Break(); + + //create a new doc type + string name = "TEST-" + Guid.NewGuid().ToString("N"); + var dt = DocumentType.MakeNew(m_User, name); + + //test the doc type + Assert.AreEqual(DateTime.Now.Date, dt.CreateDateTime.Date); + Assert.IsTrue(dt.Id > 0); + + //allow itself to be created under itself + dt.AllowedChildContentTypeIDs = new int[] { dt.Id }; + //create a tab + dt.AddVirtualTab("TEST"); + + //test the tab + var tabs = dt.getVirtualTabs.ToList(); + Assert.AreEqual(1, tabs.Count); + + //create a property + var allDataTypes = DataTypeDefinition.GetAll().ToList(); //get all definitions + dt.AddPropertyType(allDataTypes[0], "testProperty", "Test Property"); //add a property type of the first type found in the list + + //test the prop + var prop = dt.getPropertyType("testProperty"); + Assert.AreEqual("Test Property", prop.Name); + + //create 1st node + var node1 = Document.MakeNew("TEST-" + Guid.NewGuid().ToString("N"), dt, m_User, -1); + Assert.IsTrue(node1.Id > 0); + + //create 2nd node underneath node 1 + var node2 = Document.MakeNew("TEST-" + Guid.NewGuid().ToString("N"), dt, m_User, node1.Id); + Assert.IsTrue(node2.Id > 0); + Assert.AreEqual(node1.Id, node2.Parent.Id); + + //create 3rd node underneath node 2 + var node3 = Document.MakeNew("TEST-" + Guid.NewGuid().ToString("N"), dt, m_User, node2.Id); + Assert.IsTrue(node3.Id > 0); + Assert.AreEqual(node2.Id, node3.Parent.Id); + + Document.DeleteFromType(dt); + + Assert.IsFalse(Document.IsNode(node1.Id)); + Assert.IsFalse(Document.IsNode(node2.Id)); + Assert.IsFalse(Document.IsNode(node3.Id)); + + //now remove the document type created + dt.delete(); + + Assert.IsFalse(DocumentType.IsNode(dt.Id)); + } + + /// + /// This will find a document type that supports a heirarchy, create 2 root nodes, then create a child node under the first one, + /// then move it to the second one and finally delete everything that was created. + /// + [TestMethod] + public void MoveTest() + { + //first need to document type that allows other types of document types to exist underneath it + DocumentType parent = null; + DocumentType child = null; + var ids = DocumentType.getAllUniqueNodeIdsFromObjectType(DocumentType._objectType); + foreach (var id in ids) + { + var dt = new DocumentType(id); + var allowed = dt.AllowedChildContentTypeIDs.ToList(); + if (allowed.Count() > 0) + { + parent = dt; + child = new DocumentType(allowed[0]); + break; + } + } + if (parent == null || child == null) + { + throw new NotImplementedException("The umbraco install doesn't have document types that support a heirarchy"); + } + + //now that we have a parent and a child, we need to create some documents + var node1 = Document.MakeNew("FromCopy" + Guid.NewGuid().ToString("N"), parent, m_User, -1); + Assert.IsTrue(node1.Id > 0); + + var node2 = Document.MakeNew("ToCopy" + Guid.NewGuid().ToString("N"), parent, m_User, -1); + Assert.IsTrue(node2.Id > 0); + + //we now have 2 nodes in the root of the same type, we'll create a child node under node1 and move it to node2 + var childNode = Document.MakeNew("ChildCopy" + Guid.NewGuid().ToString("N"), child, m_User, node2.Id); + Assert.IsTrue(childNode.Id > 0); + + childNode.Move(node2.Id); + Assert.AreEqual(node2.Id, childNode.Parent.Id); + + RecycleAndDelete(childNode); + RecycleAndDelete(node2); + RecycleAndDelete(node1); + } + + /// + /// This will find an existing node, copy it to the same parent, delete the copied node and restore it, then finally completley remove it. + /// + [TestMethod] + public void UndeleteTest() + { + //find existing content + var doc = new Document(GetExistingNodeId()); + //create new content based on the existing content in the same heirarchy + var dt = new DocumentType(doc.ContentType.Id); + var parentId = doc.Level == 1 ? -1 : doc.Parent.Id; + var newDoc = Document.MakeNew("NewDoc" + Guid.NewGuid().ToString("N"), dt, m_User, parentId); + Assert.IsTrue(newDoc.Id > 0); + + //this will recycle the node + newDoc.delete(); + Assert.IsTrue(newDoc.IsTrashed); + Assert.IsTrue(newDoc.Path.Contains("," + (int)RecycleBin.RecycleBinType.Content + ",")); + + //undelete the node (move it) + newDoc.Move(parentId); + Assert.IsFalse(newDoc.IsTrashed); + Assert.IsFalse(newDoc.Path.Contains("," + (int)RecycleBin.RecycleBinType.Content + ",")); + + //remove it completely + RecycleAndDelete(newDoc); + } + + /// + /// This method will create 20 content nodes, send them to the recycle bin and then empty the recycle bin + /// + [TestMethod] + public void EmptyRecycleBinTest() + { + var docList = new List(); + var total = 20; + var dt = new DocumentType(GetExistingDocTypeId()); + //create 20 content nodes + for (var i = 0; i < total; i++) + { + docList.Add(Document.MakeNew(i.ToString() + Guid.NewGuid().ToString("N"), dt, m_User, -1)); + Assert.IsTrue(docList[docList.Count - 1].Id > 0); + } + + //now delete all of them + foreach (var d in docList) + { + d.delete(); + Assert.IsTrue(d.IsTrashed); + } + + //a callback action for each item removed from the recycle bin + var totalDeleted = 0; + var deleteCallback = new Action(x => + { + Assert.AreEqual(total - (++totalDeleted), x); + }); + + var bin = new RecycleBin(RecycleBin.RecycleBinType.Content); + bin.CallTheGarbageMan(deleteCallback); + + Assert.AreEqual(0, RecycleBin.Count(RecycleBin.RecycleBinType.Content)); + } + + #endregion + + #region TEST TO BE WRITTEN + + ///// + /////A test for XmlPopulate + ///// + //[TestMethod()] + //public void XmlPopulateTest() + //{ + // Guid id = new Guid(); // TODO: Initialize to an appropriate value + // Document target = new Document(id); // TODO: Initialize to an appropriate value + // XmlDocument xd = null; // TODO: Initialize to an appropriate value + // XmlNode x = null; // TODO: Initialize to an appropriate value + // XmlNode xExpected = null; // TODO: Initialize to an appropriate value + // bool Deep = false; // TODO: Initialize to an appropriate value + // target.XmlPopulate(xd, ref x, Deep); + // Assert.AreEqual(xExpected, x); + // Assert.Inconclusive("A method that does not return a value cannot be verified."); + //} + + ///// + /////A test for XmlNodeRefresh + ///// + //[TestMethod()] + //public void XmlNodeRefreshTest() + //{ + // Guid id = new Guid(); // TODO: Initialize to an appropriate value + // Document target = new Document(id); // TODO: Initialize to an appropriate value + // XmlDocument xd = null; // TODO: Initialize to an appropriate value + // XmlNode x = null; // TODO: Initialize to an appropriate value + // XmlNode xExpected = null; // TODO: Initialize to an appropriate value + // target.XmlNodeRefresh(xd, ref x); + // Assert.AreEqual(xExpected, x); + // Assert.Inconclusive("A method that does not return a value cannot be verified."); + //} + + ///// + /////A test for XmlGenerate + ///// + //[TestMethod()] + //public void XmlGenerateTest() + //{ + // Guid id = new Guid(); // TODO: Initialize to an appropriate value + // Document target = new Document(id); // TODO: Initialize to an appropriate value + // XmlDocument xd = null; // TODO: Initialize to an appropriate value + // target.XmlGenerate(xd); + // Assert.Inconclusive("A method that does not return a value cannot be verified."); + //} + + ///// + /////A test for ToXml + ///// + //[TestMethod()] + //public void ToXmlTest() + //{ + // Guid id = new Guid(); // TODO: Initialize to an appropriate value + // Document target = new Document(id); // TODO: Initialize to an appropriate value + // XmlDocument xd = null; // TODO: Initialize to an appropriate value + // bool Deep = false; // TODO: Initialize to an appropriate value + // XmlNode expected = null; // TODO: Initialize to an appropriate value + // XmlNode actual; + // actual = target.ToXml(xd, Deep); + // Assert.AreEqual(expected, actual); + // Assert.Inconclusive("Verify the correctness of this test method."); + //} + + + ///// + /////A test for SendToPublication + ///// + //[TestMethod()] + //public void SendToPublicationTest() + //{ + // Guid id = new Guid(); // TODO: Initialize to an appropriate value + // Document target = new Document(id); // TODO: Initialize to an appropriate value + // User u = null; // TODO: Initialize to an appropriate value + // bool expected = false; // TODO: Initialize to an appropriate value + // bool actual; + // actual = target.SendToPublication(u); + // Assert.AreEqual(expected, actual); + // Assert.Inconclusive("Verify the correctness of this test method."); + //} + + ///// + /////A test for RePublishAll + ///// + //[TestMethod()] + //public void RePublishAllTest() + //{ + // Document.RePublishAll(); + // Assert.Inconclusive("A method that does not return a value cannot be verified."); + //} + + ///// + /////A test for RegeneratePreviews + ///// + //[TestMethod()] + //public void RegeneratePreviewsTest() + //{ + // Document.RegeneratePreviews(); + // Assert.Inconclusive("A method that does not return a value cannot be verified."); + //} + + ///// + /////A test for refreshXmlSortOrder + ///// + //[TestMethod()] + //public void refreshXmlSortOrderTest() + //{ + // Guid id = new Guid(); // TODO: Initialize to an appropriate value + // Document target = new Document(id); // TODO: Initialize to an appropriate value + // target.refreshXmlSortOrder(); + // Assert.Inconclusive("A method that does not return a value cannot be verified."); + //} + + ///// + /////A test for PublishWithSubs + ///// + //[TestMethod()] + //public void PublishWithSubsTest() + //{ + // Guid id = new Guid(); // TODO: Initialize to an appropriate value + // Document target = new Document(id); // TODO: Initialize to an appropriate value + // User u = null; // TODO: Initialize to an appropriate value + // target.PublishWithSubs(u); + // Assert.Inconclusive("A method that does not return a value cannot be verified."); + //} + + ///// + /////A test for PublishWithResult + ///// + //[TestMethod()] + //public void PublishWithResultTest() + //{ + // Guid id = new Guid(); // TODO: Initialize to an appropriate value + // Document target = new Document(id); // TODO: Initialize to an appropriate value + // User u = null; // TODO: Initialize to an appropriate value + // bool expected = false; // TODO: Initialize to an appropriate value + // bool actual; + // actual = target.PublishWithResult(u); + // Assert.AreEqual(expected, actual); + // Assert.Inconclusive("Verify the correctness of this test method."); + //} + + ///// + /////A test for PublishWithChildrenWithResult + ///// + //[TestMethod()] + //public void PublishWithChildrenWithResultTest() + //{ + // Guid id = new Guid(); // TODO: Initialize to an appropriate value + // Document target = new Document(id); // TODO: Initialize to an appropriate value + // User u = null; // TODO: Initialize to an appropriate value + // bool expected = false; // TODO: Initialize to an appropriate value + // bool actual; + // actual = target.PublishWithChildrenWithResult(u); + // Assert.AreEqual(expected, actual); + // Assert.Inconclusive("Verify the correctness of this test method."); + //} + + ///// + /////A test for Publish + ///// + //[TestMethod()] + //public void PublishTest() + //{ + // Guid id = new Guid(); // TODO: Initialize to an appropriate value + // Document target = new Document(id); // TODO: Initialize to an appropriate value + // User u = null; // TODO: Initialize to an appropriate value + // target.Publish(u); + // Assert.Inconclusive("A method that does not return a value cannot be verified."); + //} + + ///// + /////A test for Import + ///// + //[TestMethod()] + //public void ImportTest() + //{ + // int ParentId = 0; // TODO: Initialize to an appropriate value + // User Creator = null; // TODO: Initialize to an appropriate value + // XmlElement Source = null; // TODO: Initialize to an appropriate value + // int expected = 0; // TODO: Initialize to an appropriate value + // int actual; + // actual = Document.Import(ParentId, Creator, Source); + // Assert.AreEqual(expected, actual); + // Assert.Inconclusive("Verify the correctness of this test method."); + //} + + ///// + /////A test for GetTextPath + ///// + //[TestMethod()] + //public void GetTextPathTest() + //{ + // Guid id = new Guid(); // TODO: Initialize to an appropriate value + // Document target = new Document(id); // TODO: Initialize to an appropriate value + // string expected = string.Empty; // TODO: Initialize to an appropriate value + // string actual; + // actual = target.GetTextPath(); + // Assert.AreEqual(expected, actual); + // Assert.Inconclusive("Verify the correctness of this test method."); + //} + + ///// + /////A test for GetRootDocuments + ///// + //[TestMethod()] + //public void GetRootDocumentsTest() + //{ + // Document[] expected = null; // TODO: Initialize to an appropriate value + // Document[] actual; + // actual = Document.GetRootDocuments(); + // Assert.AreEqual(expected, actual); + // Assert.Inconclusive("Verify the correctness of this test method."); + //} + + ///// + /////A test for GetNodesForPreview + ///// + //[TestMethod()] + //public void GetNodesForPreviewTest() + //{ + // Guid id = new Guid(); // TODO: Initialize to an appropriate value + // Document target = new Document(id); // TODO: Initialize to an appropriate value + // bool childrenOnly = false; // TODO: Initialize to an appropriate value + // List expected = null; // TODO: Initialize to an appropriate value + // List actual; + // actual = target.GetNodesForPreview(childrenOnly); + // Assert.AreEqual(expected, actual); + // Assert.Inconclusive("Verify the correctness of this test method."); + //} + + ///// + /////A test for GetDocumentsForRelease + ///// + //[TestMethod()] + //public void GetDocumentsForReleaseTest() + //{ + // Document[] expected = null; // TODO: Initialize to an appropriate value + // Document[] actual; + // actual = Document.GetDocumentsForRelease(); + // Assert.AreEqual(expected, actual); + // Assert.Inconclusive("Verify the correctness of this test method."); + //} + + ///// + /////A test for GetDocumentsForExpiration + ///// + //[TestMethod()] + //public void GetDocumentsForExpirationTest() + //{ + // Document[] expected = null; // TODO: Initialize to an appropriate value + // Document[] actual; + // actual = Document.GetDocumentsForExpiration(); + // Assert.AreEqual(expected, actual); + // Assert.Inconclusive("Verify the correctness of this test method."); + //} + + ///// + /////A test for GetChildrenForTree + ///// + //[TestMethod()] + //public void GetChildrenForTreeTest() + //{ + // int NodeId = 0; // TODO: Initialize to an appropriate value + // Document[] expected = null; // TODO: Initialize to an appropriate value + // Document[] actual; + // actual = Document.GetChildrenForTree(NodeId); + // Assert.AreEqual(expected, actual); + // Assert.Inconclusive("Verify the correctness of this test method."); + //} + + ///// + /////A test for CountSubs + ///// + //[TestMethod()] + //public void CountSubsTest() + //{ + // int parentId = 0; // TODO: Initialize to an appropriate value + // bool publishedOnly = false; // TODO: Initialize to an appropriate value + // int expected = 0; // TODO: Initialize to an appropriate value + // int actual; + // actual = Document.CountSubs(parentId, publishedOnly); + // Assert.AreEqual(expected, actual); + // Assert.Inconclusive("Verify the correctness of this test method."); + //} + + ///// + /////A test for Copy + ///// + //[TestMethod()] + //public void CopyTest1() + //{ + // Guid id = new Guid(); // TODO: Initialize to an appropriate value + // Document target = new Document(id); // TODO: Initialize to an appropriate value + // int CopyTo = 0; // TODO: Initialize to an appropriate value + // User u = null; // TODO: Initialize to an appropriate value + // target.Copy(CopyTo, u); + // Assert.Inconclusive("A method that does not return a value cannot be verified."); + //} + + #endregion + + #region Private properties and methods + private User m_User = new User(0); + + private void RecycleAndDelete(Document d) + { + var id = d.Id; + //now recycle it + d.delete(); + + Assert.IsTrue(d.IsTrashed); + + Document recycled = new Document(id); + //now delete it + recycled.delete(); + + Assert.IsFalse(Document.IsNode(id)); + + //check with sql that it is gone + var count = Application.SqlHelper.ExecuteScalar("SELECT COUNT(*) FROM umbracoNode WHERE id=@id", + Application.SqlHelper.CreateParameter("@id", id)); + + Assert.AreEqual(0, count); + } + + /// + /// Returns a random docuemnt type that supports a text property + /// + /// + private int GetExistingDocTypeId() + { + var types = DocumentType.GetAllAsList(); + DocumentType found = null; + TextFieldDataType txtField = new TextFieldDataType(); + foreach (var d in types) + { + var prop = d.PropertyTypes + .Where(x => x.DataTypeDefinition.DataType.Id == txtField.Id).FirstOrDefault(); + if (prop != null) + { + found = d; + break; + } + } + if (found == null) + { + throw new MissingMemberException("No document type was found that contains a text field property"); + } + return found.Id; + } + + /// + /// Returns a text field property of the document type specified. This will throw an exception if one is not found. + /// + /// + /// + private Property GetTextFieldProperty(DocumentType dt, Document d) + { + TextFieldDataType txtField = new TextFieldDataType(); + var prop = dt.PropertyTypes + .Where(x => x.DataTypeDefinition.DataType.Id == txtField.Id).First(); + return d.GenericProperties.Where(x => x.PropertyType.Id == prop.Id).First(); + } + + /// + /// Returns a content node + /// + /// + private int GetExistingNodeId() + { + var ids = Document.getAllUniqueNodeIdsFromObjectType(Document._objectType).ToList(); + var r = new Random(); + var index = r.Next(0, ids.Count() - 1); + return ids[index]; + } + #endregion + + #region Test Context + private TestContext testContextInstance; + + /// + ///Gets or sets the test context which provides + ///information about and functionality for the current test run. + /// + public TestContext TestContext + { + get + { + return testContextInstance; + } + set + { + testContextInstance = value; + } + } + #endregion + + #region Initialize and cleanup + // + //You can use the following additional attributes as you write your tests: + // + //Use ClassInitialize to run code before running the first test in the class + //[ClassInitialize()] + //public static void MyClassInitialize(TestContext testContext) + //{ + //} + // + //Use ClassCleanup to run code after all tests in a class have run + //[ClassCleanup()] + //public static void MyClassCleanup() + //{ + //} + // + //Use TestInitialize to run code before running each test + //[TestInitialize()] + //public void MyTestInitialize() + //{ + //} + // + //Use TestCleanup to run code after each test has run + //[TestCleanup()] + //public void MyTestCleanup() + //{ + //} + // + #endregion + } +} diff --git a/umbraco.Test/Properties/AssemblyInfo.cs b/umbraco.Test/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..69339e7adc --- /dev/null +++ b/umbraco.Test/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("umbraco.Test")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("umbraco.Test")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM componenets. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("662e3518-c5af-49ea-a195-dee4b94a522d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/umbraco.Test/Test References/umbraco.accessor b/umbraco.Test/Test References/umbraco.accessor new file mode 100644 index 0000000000..6939496894 --- /dev/null +++ b/umbraco.Test/Test References/umbraco.accessor @@ -0,0 +1,2 @@ +umbraco.dll +Desktop diff --git a/umbraco.Test/umbraco.Test.csproj b/umbraco.Test/umbraco.Test.csproj new file mode 100644 index 0000000000..ecbfcdf873 --- /dev/null +++ b/umbraco.Test/umbraco.Test.csproj @@ -0,0 +1,172 @@ + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {6277C9FB-3A9A-4537-AA86-82DA9B2527FD} + Library + Properties + umbraco.Test + umbraco.Test + v3.5 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + SAK + SAK + SAK + SAK + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + False + ..\foreign dlls\ClientDependency.Core.dll + + + False + ..\foreign dlls\CookComputing.XmlRpcV2.dll + + + False + ..\foreign dlls\Examine.dll + + + False + ..\foreign dlls\ICSharpCode.SharpZipLib.dll + + + False + ..\foreign dlls\IronPython.dll + + + False + ..\foreign dlls\IronRuby.dll + + + False + ..\foreign dlls\Microsoft.Scripting.dll + + + False + ..\foreign dlls\Microsoft.Scripting.Core.dll + + + False + ..\foreign dlls\Microsoft.Scripting.ExtensionAttribute.dll + + + + + + 3.5 + + + + 3.5 + + + + + + + + 3.5 + + + 3.5 + + + 3.5 + + + + + 3.5 + + + False + ..\foreign dlls\TidyNet.dll + + + False + ..\foreign dlls\UmbracoExamine.dll + + + False + ..\foreign dlls\UrlRewritingNet.UrlRewriter.dll + + + + + + + + + + + + + + + {255F5DF1-4E43-4758-AC05-7A0B68EB021B} + umbraco.editorControls + + + {52AB8F1F-FB76-4E8C-885F-0747B6CE71EC} + umbraco.macroRenderings + + + {6EDD2061-82F2-461B-BB6E-879245A832DE} + umbraco.controls + + + {E469A9CE-1BEC-423F-AC44-713CD72457EA} + umbraco.businesslogic + + + {CCD75EC3-63DB-4184-B49D-51C1DD337230} + umbraco.cms + + + {C7CB79F0-1C97-4B33-BFA7-00731B579AE2} + umbraco.datalayer + + + {511F6D8D-7717-440A-9A57-A507E9A8B27F} + umbraco.interfaces + + + {651E1350-91B6-44B7-BD60-7207006D7003} + umbraco.presentation + + + {D7636876-0756-43CB-A192-138C6F0D5E42} + umbraco.providers + + + + + \ No newline at end of file diff --git a/umbraco.Test/umbraco.Test.csproj.vspscc b/umbraco.Test/umbraco.Test.csproj.vspscc new file mode 100644 index 0000000000..feffdecaa4 --- /dev/null +++ b/umbraco.Test/umbraco.Test.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/umbraco.sln b/umbraco.sln index 89e67803e8..874fbc6ec7 100644 --- a/umbraco.sln +++ b/umbraco.sln @@ -4,8 +4,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject build.xml = build.xml default.build = default.build + SHANDEMVAIO.testrunconfig = SHANDEMVAIO.testrunconfig umbraco weekly.build = umbraco weekly.build umbraco.build = umbraco.build + umbraco.vsmdi = umbraco.vsmdi EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "StandardConfig", "StandardConfig", "{B8ECDB8B-BF44-462C-B6DA-22421B3EC4D7}" @@ -98,9 +100,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DLLs", "DLLs", "{F06D18F6-C foreign dlls\VistaDB.NET20.dll = foreign dlls\VistaDB.NET20.dll EndProjectSection EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "umbraco.Test", "umbraco.Test\umbraco.Test.csproj", "{6277C9FB-3A9A-4537-AA86-82DA9B2527FD}" +EndProject Global GlobalSection(TeamFoundationVersionControl) = preSolution - SccNumberOfProjects = 13 + SccNumberOfProjects = 14 SccEnterpriseProvider = {4CA58AB2-18FA-4F8D-95D4-32DDF27D184C} SccTeamFoundationServer = https://tfs01.codeplex.com/ SccLocalPath0 = . @@ -140,6 +144,12 @@ Global SccProjectUniqueName12 = umbraco\\umbraco.Legacy\\umbraco.Legacy.csproj SccProjectName12 = umbraco/umbraco.Legacy SccLocalPath12 = umbraco\\umbraco.Legacy + SccProjectUniqueName13 = umbraco.Test\\umbraco.Test.csproj + SccProjectName13 = umbraco.Test + SccLocalPath13 = umbraco.Test + EndGlobalSection + GlobalSection(TestCaseManagementSettings) = postSolution + CategoryFile = umbraco.vsmdi EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug - Fixed Version|Any CPU = Debug - Fixed Version|Any CPU @@ -216,6 +226,12 @@ Global {27A2590E-1313-4A33-89FD-92811540B69C}.Debug - Fixed Version|Any CPU.ActiveCfg = Debug|Any CPU {27A2590E-1313-4A33-89FD-92811540B69C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {27A2590E-1313-4A33-89FD-92811540B69C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6277C9FB-3A9A-4537-AA86-82DA9B2527FD}.Debug - Fixed Version|Any CPU.ActiveCfg = Debug|Any CPU + {6277C9FB-3A9A-4537-AA86-82DA9B2527FD}.Debug - Fixed Version|Any CPU.Build.0 = Debug|Any CPU + {6277C9FB-3A9A-4537-AA86-82DA9B2527FD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6277C9FB-3A9A-4537-AA86-82DA9B2527FD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6277C9FB-3A9A-4537-AA86-82DA9B2527FD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6277C9FB-3A9A-4537-AA86-82DA9B2527FD}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/umbraco.vsmdi b/umbraco.vsmdi new file mode 100644 index 0000000000..d85bbd66f8 --- /dev/null +++ b/umbraco.vsmdi @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/umbraco/cms/businesslogic/CMSNode.cs b/umbraco/cms/businesslogic/CMSNode.cs index 0d099fd145..ad43abcca8 100644 --- a/umbraco/cms/businesslogic/CMSNode.cs +++ b/umbraco/cms/businesslogic/CMSNode.cs @@ -11,6 +11,7 @@ using System.IO; using System.Text.RegularExpressions; using System.ComponentModel; using umbraco.IO; +using umbraco.cms.businesslogic.media; namespace umbraco.cms.businesslogic { @@ -44,7 +45,8 @@ namespace umbraco.cms.businesslogic private int _userId; private DateTime _createDate; private bool _hasChildrenInitialized; - private string m_image = "default.png"; + private string m_image = "default.png"; + private bool? _isTrashed = null; #endregion @@ -132,7 +134,7 @@ namespace umbraco.cms.businesslogic /// True if there is a CMSNode with the given id public static bool IsNode(int Id) { - return (SqlHelper.ExecuteScalar("select count(id) from umbracoNode where id = '" + Id + "'") > 0); + return (SqlHelper.ExecuteScalar("select count(id) from umbracoNode where id = @id", SqlHelper.CreateParameter("@id", Id)) > 0); } /// @@ -231,7 +233,7 @@ namespace umbraco.cms.businesslogic // But does anyone know what the 'level++' is supposed to be doing there? // Nothing obviously, since it's a postfix. - SqlHelper.ExecuteNonQuery("INSERT INTO umbracoNode(trashed, parentID, nodeObjectType, nodeUser, level, path, sortOrder, uniqueID, text) VALUES(@trashed, @parentID, @nodeObjectType, @nodeUser, @level, @path, @sortOrder, @uniqueID, @text)", + SqlHelper.ExecuteNonQuery("INSERT INTO umbracoNode(trashed, parentID, nodeObjectType, nodeUser, level, path, sortOrder, uniqueID, text, createDate) VALUES(@trashed, @parentID, @nodeObjectType, @nodeUser, @level, @path, @sortOrder, @uniqueID, @text, @createDate)", SqlHelper.CreateParameter("@trashed", 0), SqlHelper.CreateParameter("@parentID", parentId), SqlHelper.CreateParameter("@nodeObjectType", objectType), @@ -240,7 +242,8 @@ namespace umbraco.cms.businesslogic SqlHelper.CreateParameter("@path", path), SqlHelper.CreateParameter("@sortOrder", sortOrder), SqlHelper.CreateParameter("@uniqueID", uniqueID), - SqlHelper.CreateParameter("@text", text)); + SqlHelper.CreateParameter("@text", text), + SqlHelper.CreateParameter("@createDate", DateTime.Now)); CMSNode retVal = new CMSNode(uniqueID); retVal.Path = path + "," + retVal.Id.ToString(); @@ -334,6 +337,49 @@ namespace umbraco.cms.businesslogic #region Public Methods + + /// + /// An xml representation of the CMSNOde + /// + /// Xmldocument context + /// If true the xml will append the CMSNodes child xml + /// The CMSNode Xmlrepresentation + public virtual XmlNode ToXml(XmlDocument xd, bool Deep) + { + XmlNode x = xd.CreateNode(XmlNodeType.Element, "node", ""); + XmlPopulate(xd, x, Deep); + return x; + } + + public virtual XmlNode ToPreviewXml(XmlDocument xd) + { + // If xml already exists + if (!PreviewExists(UniqueId)) + { + savePreviewXml(ToXml(xd, false), UniqueId); + } + return GetPreviewXml(xd, UniqueId); + } + + public virtual List GetNodesForPreview(bool childrenOnly) + { + List nodes = new List(); + string sql = @" +select umbracoNode.id, umbracoNode.parentId, umbracoNode.level, umbracoNode.sortOrder, cmsPreviewXml.xml from umbracoNode +inner join cmsPreviewXml on cmsPreviewXml.nodeId = umbracoNode.id +where trashed = 0 and path like '{0}' +order by level,sortOrder"; + + string pathExp = childrenOnly ? Path + ",%" : Path; + + IRecordsReader dr = SqlHelper.ExecuteReader(String.Format(sql, pathExp)); + while (dr.Read()) + nodes.Add(new CMSPreviewNode(dr.GetInt("id"), dr.GetGuid("uniqueID"), dr.GetInt("parentId"), dr.GetShort("level"), dr.GetInt("sortOrder"), dr.GetString("xml"))); + dr.Close(); + + return nodes; + } + /// /// Used to persist object changes to the database. In Version3.0 it's just a stub for future compatibility /// @@ -360,46 +406,74 @@ namespace umbraco.cms.businesslogic } return base.ToString(); - } + } /// /// Moves the CMSNode from the current position in the hierarchy to the target /// /// Target CMSNode id - public void Move(int NewParentId) + public void Move(int newParentId) { + //first we need to establish if the node already exists under the parent node + var isSameParent = (Path.Contains("," + newParentId + ",")); + MoveEventArgs e = new MoveEventArgs(); FireBeforeMove(e); if (!e.Cancel) { - int maxSortOrder = SqlHelper.ExecuteScalar( + CMSNode n = new CMSNode(newParentId); + + //if it's the same parent, we can save some SQL calls since we know these wont change. + //level and path might change even if it's the same parent because the parent could be moving somewhere. + if (!isSameParent) + { + int maxSortOrder = SqlHelper.ExecuteScalar( "select coalesce(max(sortOrder),0) from umbracoNode where parentid = @parentId", - SqlHelper.CreateParameter("@parentId", NewParentId)); + SqlHelper.CreateParameter("@parentId", newParentId)); - - CMSNode n = new CMSNode(NewParentId); - this.Parent = n; + this.Parent = n; + this.sortOrder = maxSortOrder + 1; + } + this.Level = n.Level + 1; this.Path = n.Path + "," + this.Id.ToString(); - this.sortOrder = maxSortOrder + 1; + //this code block should not be here but since the class structure is very poor and doesn't use + //overrides (instead using shadows/new) for the Children property, when iterating over the children + //and calling Move(), the super classes overridden OnMove or Move methods never get fired, so + //we now need to hard code this here :( - - if (n.nodeObjectType == web.Document._objectType) + //make sure the node type is a document/media, if it is a recycle bin then this will not be equal + if (n.nodeObjectType == Document._objectType) { - Document d = - new umbraco.cms.businesslogic.web.Document(n.Id); + //regenerate the xml for the parent node + var d = new Document(n.Id); d.XmlGenerate(new XmlDocument()); - } - else if (n.nodeObjectType == media.Media._objectType) - new umbraco.cms.businesslogic.media.Media(n.Id).XmlGenerate(new XmlDocument()); + else if (n.nodeObjectType == Media._objectType) + { + //regenerate the xml for the parent node + var m = new Media(n.Id); + m.XmlGenerate(new XmlDocument()); + } + + if (Path.Contains("," + ((int)RecycleBin.RecycleBinType.Content).ToString() + ",") + || Path.Contains("," + ((int)RecycleBin.RecycleBinType.Media).ToString() + ",")) + { + //if we've moved this to the recyle bin, we need to update the trashed property + if (!IsTrashed) IsTrashed = true; //don't update if it's not necessary + } + else + { + if (IsTrashed) IsTrashed = false; //don't update if it's not necessary + } - //store children array here because iterating over an Array property object is very inneficient. var children = this.Children; foreach (CMSNode c in children) + { c.Move(this.Id); + } FireAfterMove(e); } @@ -454,47 +528,28 @@ namespace umbraco.cms.businesslogic #region Public properties - /// - /// An xml representation of the CMSNOde + /// Determines if the node is in the recycle bin. + /// This is only relavent for node types that support a recyle bin (such as Document/Media) /// - /// Xmldocument context - /// If true the xml will append the CMSNodes child xml - /// The CMSNode Xmlrepresentation - public virtual XmlNode ToXml(XmlDocument xd, bool Deep) + public bool IsTrashed { - XmlNode x = xd.CreateNode(XmlNodeType.Element, "node", ""); - XmlPopulate(xd, x, Deep); - return x; - } - - public virtual XmlNode ToPreviewXml(XmlDocument xd) - { - // If xml already exists - if (!PreviewExists(UniqueId)) + get { - savePreviewXml(ToXml(xd, false), UniqueId); + if (!_isTrashed.HasValue) + { + _isTrashed = Convert.ToBoolean(SqlHelper.ExecuteScalar("SELECT trashed FROM umbracoNode where id=@id", + SqlHelper.CreateParameter("@id", this.Id))); + } + return _isTrashed.Value; + } + set + { + _isTrashed = value; + SqlHelper.ExecuteNonQuery("update umbracoNode set trashed = @trashed where id = @id", + SqlHelper.CreateParameter("@trashed", value), + SqlHelper.CreateParameter("@id", this.Id)); } - return GetPreviewXml(xd, UniqueId); - } - - public virtual List GetNodesForPreview(bool childrenOnly) - { - List nodes = new List(); - string sql = @" -select umbracoNode.id, umbracoNode.parentId, umbracoNode.level, umbracoNode.sortOrder, cmsPreviewXml.xml from umbracoNode -inner join cmsPreviewXml on cmsPreviewXml.nodeId = umbracoNode.id -where trashed = 0 and path like '{0}' -order by level,sortOrder"; - - string pathExp = childrenOnly ? Path + ",%" : Path; - - IRecordsReader dr = SqlHelper.ExecuteReader(String.Format(sql, pathExp)); - while (dr.Read()) - nodes.Add(new CMSPreviewNode(dr.GetInt("id"), dr.GetGuid("uniqueID"), dr.GetInt("parentId"), dr.GetShort("level"), dr.GetInt("sortOrder"), dr.GetString("xml"))); - dr.Close(); - - return nodes; } /// @@ -511,7 +566,6 @@ order by level,sortOrder"; } } - /// /// Gets or sets the create date time. /// @@ -526,7 +580,6 @@ order by level,sortOrder"; } } - /// /// Gets the creator /// @@ -635,21 +688,19 @@ order by level,sortOrder"; get { System.Collections.ArrayList tmp = new System.Collections.ArrayList(); - IRecordsReader dr = SqlHelper.ExecuteReader("SELECT id, createDate, trashed, parentId, nodeObjectType, nodeUser, level, path, sortOrder, uniqueID, text FROM umbracoNode WHERE ParentID = @ParentID AND nodeObjectType = @type order by sortOrder", - SqlHelper.CreateParameter("@type", this.nodeObjectType), SqlHelper.CreateParameter("ParentID", this.Id)); - while (dr.Read()) + using (IRecordsReader dr = SqlHelper.ExecuteReader("SELECT id, createDate, trashed, parentId, nodeObjectType, nodeUser, level, path, sortOrder, uniqueID, text FROM umbracoNode WHERE ParentID = @ParentID AND nodeObjectType = @type order by sortOrder", + SqlHelper.CreateParameter("@type", this.nodeObjectType), SqlHelper.CreateParameter("ParentID", this.Id))) { - ; - tmp.Add(new CMSNode(dr)); + while (dr.Read()) + { + tmp.Add(new CMSNode(dr)); + } } - - dr.Close(); - + CMSNode[] retval = new CMSNode[tmp.Count]; for (int i = 0; i < tmp.Count; i++) { - //retval[i] = new CMSNode((int)tmp[i]); retval[i] = (CMSNode)tmp[i]; } return retval; @@ -818,12 +869,14 @@ order by level,sortOrder"; { XmlDocument xmlDoc = new XmlDocument(); - XmlReader xmlRdr = SqlHelper.ExecuteXmlReader( + using (XmlReader xmlRdr = SqlHelper.ExecuteXmlReader( "select xml from cmsPreviewXml where nodeID = @nodeId and versionId = @versionId", SqlHelper.CreateParameter("@nodeId", Id), - SqlHelper.CreateParameter("@versionId", version)); - xmlDoc.Load(xmlRdr); - + SqlHelper.CreateParameter("@versionId", version))) + { + xmlDoc.Load(xmlRdr); + } + return xd.ImportNode(xmlDoc.FirstChild, true); } @@ -861,6 +914,7 @@ order by level,sortOrder"; _sortOrder = dr.GetInt("sortOrder"); _userId = dr.GetInt("nodeUser"); _createDate = dr.GetDateTime("createDate"); + _isTrashed = dr.GetBoolean("trashed"); } #endregion diff --git a/umbraco/cms/businesslogic/Content.cs b/umbraco/cms/businesslogic/Content.cs index 98dd7341b9..3c8a85bf56 100644 --- a/umbraco/cms/businesslogic/Content.cs +++ b/umbraco/cms/businesslogic/Content.cs @@ -459,12 +459,12 @@ namespace umbraco.cms.businesslogic // Delete version history SqlHelper.ExecuteNonQuery("Delete from cmsContentVersion where ContentId = " + this.Id); - // Delete Contentspecific data () - SqlHelper.ExecuteNonQuery("Delete from cmsContent where NodeId = " + this.Id); - // Delete xml SqlHelper.ExecuteNonQuery("delete from cmsContentXml where nodeID = @nodeId", SqlHelper.CreateParameter("@nodeId", this.Id)); + // Delete Contentspecific data () + SqlHelper.ExecuteNonQuery("Delete from cmsContent where NodeId = " + this.Id); + // Delete Nodeinformation!! base.delete(); } @@ -646,7 +646,7 @@ namespace umbraco.cms.businesslogic /// /// Optimized method for bulk deletion of propertieson a Content object. /// - private void deleteAllProperties() + protected void deleteAllProperties() { SqlHelper.ExecuteNonQuery("Delete from cmsPropertyData where contentNodeId = @nodeId", SqlHelper.CreateParameter("@nodeId", this.Id)); } diff --git a/umbraco/cms/businesslogic/ContentType.cs b/umbraco/cms/businesslogic/ContentType.cs index 50a85e62f1..4006521184 100644 --- a/umbraco/cms/businesslogic/ContentType.cs +++ b/umbraco/cms/businesslogic/ContentType.cs @@ -284,6 +284,8 @@ namespace umbraco.cms.businesslogic } } + //THIS SHOULD BE IENUMERABLE NOT LIST! + /// /// The "datafield/column" definitions, a Content object of this type will have an equivalent /// list of Properties. @@ -669,6 +671,11 @@ namespace umbraco.cms.businesslogic } } + //need to delete the allowed relationships between content types + SqlHelper.ExecuteNonQuery("delete from cmsContentTypeAllowedContentType where AllowedId=@allowedId or Id=@id", + SqlHelper.CreateParameter("@allowedId", Id), + SqlHelper.CreateParameter("@id", Id)); + // delete contenttype entrance SqlHelper.ExecuteNonQuery("Delete from cmsContentType where NodeId = " + Id); diff --git a/umbraco/cms/businesslogic/Property/Property.cs b/umbraco/cms/businesslogic/Property/Property.cs index 23b51b4fc4..aec8ab3f83 100644 --- a/umbraco/cms/businesslogic/Property/Property.cs +++ b/umbraco/cms/businesslogic/Property/Property.cs @@ -9,70 +9,78 @@ using umbraco.BusinessLogic; namespace umbraco.cms.businesslogic.property { - /// - /// Property class encapsulates property factory, ensuring that the work - /// with umbraco generic properties stays nice and easy.. - /// - public class Property - { - private static string _connstring = GlobalSettings.DbDSN; - propertytype.PropertyType _pt; - interfaces.IData _data; - private int _id; + /// + /// Property class encapsulates property factory, ensuring that the work + /// with umbraco generic properties stays nice and easy.. + /// + public class Property + { + private static string _connstring = GlobalSettings.DbDSN; + propertytype.PropertyType _pt; + interfaces.IData _data; + private int _id; protected static ISqlHelper SqlHelper { get { return Application.SqlHelper; } } - - public Property(int Id, propertytype.PropertyType pt) - { - - _pt = pt; - _id = Id; - _data = _pt.DataTypeDefinition.DataType.Data; - _data.PropertyId = Id; - } - public Property(int Id) - { - _id = Id; - _pt = umbraco.cms.businesslogic.propertytype.PropertyType.GetPropertyType( - SqlHelper.ExecuteScalar("select propertytypeid from cmsPropertyData where id = @id", SqlHelper.CreateParameter("@id", Id))); - _data = _pt.DataTypeDefinition.DataType.Data; - _data.PropertyId = Id; - } + public Property(int Id, propertytype.PropertyType pt) + { - public Guid VersionId - { - get - { - IRecordsReader dr = SqlHelper.ExecuteReader("Select versionId from cmsPropertyData where id = " + _id.ToString()); - dr.Read(); - return dr.GetGuid("versionId"); - } - } - public int Id { - get {return _id;} - } - public propertytype.PropertyType PropertyType { - get{return _pt;} - } + _pt = pt; + _id = Id; + _data = _pt.DataTypeDefinition.DataType.Data; + _data.PropertyId = Id; + } - public object Value { - get { - return _data.Value; - } - set { - _data.Value = value; - } - } + public Property(int Id) + { + _id = Id; + _pt = umbraco.cms.businesslogic.propertytype.PropertyType.GetPropertyType( + SqlHelper.ExecuteScalar("select propertytypeid from cmsPropertyData where id = @id", SqlHelper.CreateParameter("@id", Id))); + _data = _pt.DataTypeDefinition.DataType.Data; + _data.PropertyId = Id; + } - public void delete() { - int contentId = SqlHelper.ExecuteScalar("Select contentNodeId from cmsPropertyData where Id = " + _id); - SqlHelper.ExecuteNonQuery( "Delete from cmsPropertyData where PropertyTypeId =" + _pt.Id + " And contentNodeId = "+ contentId); - _data.Delete(); - } + public Guid VersionId + { + get + { + using (IRecordsReader dr = SqlHelper.ExecuteReader("SELECT versionId FROM cmsPropertyData WHERE id = " + _id.ToString())) + { + dr.Read(); + return dr.GetGuid("versionId"); + } + } + } + public int Id + { + get { return _id; } + } + public propertytype.PropertyType PropertyType + { + get { return _pt; } + } + + public object Value + { + get + { + return _data.Value; + } + set + { + _data.Value = value; + } + } + + public void delete() + { + int contentId = SqlHelper.ExecuteScalar("Select contentNodeId from cmsPropertyData where Id = " + _id); + SqlHelper.ExecuteNonQuery("Delete from cmsPropertyData where PropertyTypeId =" + _pt.Id + " And contentNodeId = " + contentId); + _data.Delete(); + } public XmlNode ToXml(XmlDocument xd) { string nodeName = UmbracoSettings.UseLegacyXmlSchema ? "data" : helpers.Casing.SafeAlias(PropertyType.Alias); @@ -93,8 +101,8 @@ namespace umbraco.cms.businesslogic.property [MethodImpl(MethodImplOptions.Synchronized)] - public static Property MakeNew(propertytype.PropertyType pt, Content c, Guid versionId) - { + public static Property MakeNew(propertytype.PropertyType pt, Content c, Guid versionId) + { int newPropertyId = 0; // The method is synchronized SqlHelper.ExecuteNonQuery("INSERT INTO cmsPropertyData (contentNodeId, versionId, propertyTypeId) VALUES(@contentNodeId, @versionId, @propertyTypeId)", @@ -102,10 +110,10 @@ namespace umbraco.cms.businesslogic.property SqlHelper.CreateParameter("@versionId", versionId), SqlHelper.CreateParameter("@propertyTypeId", pt.Id)); newPropertyId = SqlHelper.ExecuteScalar("SELECT MAX(id) FROM cmsPropertyData"); - interfaces.IData d = pt.DataTypeDefinition.DataType.Data; - d.MakeNew(newPropertyId); - return new Property(newPropertyId, pt); - } - } + interfaces.IData d = pt.DataTypeDefinition.DataType.Data; + d.MakeNew(newPropertyId); + return new Property(newPropertyId, pt); + } + } } \ No newline at end of file diff --git a/umbraco/cms/businesslogic/RecycleBin.cs b/umbraco/cms/businesslogic/RecycleBin.cs index c562431471..a7aea12c35 100644 --- a/umbraco/cms/businesslogic/RecycleBin.cs +++ b/umbraco/cms/businesslogic/RecycleBin.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Text; - +using System.Linq; using umbraco.DataLayer; using umbraco.cms.businesslogic.web; using umbraco.cms.businesslogic.media; @@ -23,17 +23,29 @@ namespace umbraco.cms.businesslogic Media = -21 } - private Guid _nodeObjectType; + private const string m_ChildCountSQL = @"select count(id) from umbracoNode where nodeObjectType = @nodeObjectType and parentId = @parentId"; + private const string m_ChildSQL = @"SELECT id, createDate, trashed, parentId, nodeObjectType, nodeUser, level, path, sortOrder, uniqueID, text FROM umbracoNode where ParentID = @parentId And nodeObjectType = @type order by sortOrder"; + private static object m_Locker = new object(); + #region Private variables + + private Guid _nodeObjectType; + private RecycleBinType m_BinType; + + #endregion + + #region Constructors /// /// Constructor to create a new recycle bin /// /// /// + [Obsolete("Use the simple constructor that has the RecycleBinType only parameter")] public RecycleBin(Guid nodeObjectType, RecycleBinType type) : base((int)type) { _nodeObjectType = nodeObjectType; + m_BinType = type; } /// @@ -48,9 +60,11 @@ namespace umbraco.cms.businesslogic { case RecycleBinType.Content: _nodeObjectType = Document._objectType; + m_BinType = RecycleBinType.Content; break; case RecycleBinType.Media: _nodeObjectType = Media._objectType; + m_BinType = RecycleBinType.Media; break; } @@ -60,46 +74,12 @@ namespace umbraco.cms.businesslogic /// Old constructor to create a content recycle bin /// /// - [Obsolete("Use the other constructors instead")] + [Obsolete("Use the simple constructor that has the RecycleBinType only parameter")] public RecycleBin(Guid nodeObjectType) - : this(nodeObjectType, RecycleBinType.Content) { } - - - /// - /// If I smell, I'm not empty - /// - public bool Smells() - { - return (SqlHelper.ExecuteScalar( - "select count(id) from umbracoNode where nodeObjectType = @nodeObjectType and parentId = @parentId", - SqlHelper.CreateParameter("@parentId", this.Id), - SqlHelper.CreateParameter("@nodeObjectType", _nodeObjectType)) > 0); - - } - - public override umbraco.BusinessLogic.console.IconI[] Children - { - get - { - System.Collections.ArrayList tmp = new System.Collections.ArrayList(); - - IRecordsReader dr = SqlHelper.ExecuteReader("select id from umbracoNode where ParentID = " + this.Id + " And nodeObjectType = @type order by sortOrder", - SqlHelper.CreateParameter("@type", _nodeObjectType)); - - while (dr.Read()) - tmp.Add(dr.GetInt("Id")); - - dr.Close(); - - CMSNode[] retval = new CMSNode[tmp.Count]; - - for (int i = 0; i < tmp.Count; i++) - retval[i] = new CMSNode((int)tmp[i]); - - return retval; - } - } + : this(nodeObjectType, RecycleBinType.Content) { } + #endregion + #region Static methods /// /// Get the number of items in the Recycle Bin /// @@ -107,15 +87,99 @@ namespace umbraco.cms.businesslogic [Obsolete("Create a RecycleBin object to get the count per recycle bin type", true)] public static int Count() { - return SqlHelper.ExecuteScalar("select count(id) from umbracoNode where path like '%,-20,%'"); + Guid objectType = Document._objectType; + + return SqlHelper.ExecuteScalar( + RecycleBin.m_ChildCountSQL, + SqlHelper.CreateParameter("@parentId", (int)RecycleBinType.Content), + SqlHelper.CreateParameter("@nodeObjectType", objectType)); } public static int Count(RecycleBinType type) { - string sql = String.Format("select count(id) from umbracoNode where path like '%,{0},%'", (int)type); - return SqlHelper.ExecuteScalar(sql); + Guid objectType = Document._objectType; + + switch (type) + { + case RecycleBinType.Content: + objectType = Document._objectType; + break; + case RecycleBinType.Media: + objectType = Media._objectType; + break; + } + + return SqlHelper.ExecuteScalar( + RecycleBin.m_ChildCountSQL, + SqlHelper.CreateParameter("@parentId", (int)type), + SqlHelper.CreateParameter("@nodeObjectType", objectType)); + } + #endregion + + #region Public methods + + /// + /// If I smell, I'm not empty + /// + public bool Smells() + { + return RecycleBin.Count(m_BinType) > 0; } + /// + /// Empties the trash can + /// + /// a function to call whenever an item is removed from the bin + public void CallTheGarbageMan(Action itemDeletedCallback) + { + lock (m_Locker) + { + foreach (var c in Children.ToList()) + { + switch (m_BinType) + { + case RecycleBinType.Content: + new Document(c.Id).delete(); + itemDeletedCallback(RecycleBin.Count(m_BinType)); + break; + case RecycleBinType.Media: + new Media(c.Id).delete(); + itemDeletedCallback(RecycleBin.Count(m_BinType)); + break; + } + } + } + } + + #endregion + + #region Public properties + public override umbraco.BusinessLogic.console.IconI[] Children + { + get + { + System.Collections.ArrayList tmp = new System.Collections.ArrayList(); + + using (IRecordsReader dr = SqlHelper.ExecuteReader(m_ChildSQL, + SqlHelper.CreateParameter("@parentId", this.Id), + SqlHelper.CreateParameter("@type", _nodeObjectType))) + { + while (dr.Read()) + { + tmp.Add(new CMSNode(dr)); + } + } + + CMSNode[] retval = new CMSNode[tmp.Count]; + + for (int i = 0; i < tmp.Count; i++) + { + retval[i] = (CMSNode)tmp[i]; + } + return retval; + } + } + #endregion } } diff --git a/umbraco/cms/businesslogic/datatype/DataType.cs b/umbraco/cms/businesslogic/datatype/DataTypeDefinition.cs similarity index 59% rename from umbraco/cms/businesslogic/datatype/DataType.cs rename to umbraco/cms/businesslogic/datatype/DataTypeDefinition.cs index 01dcb79dd1..2baae60a5c 100644 --- a/umbraco/cms/businesslogic/datatype/DataType.cs +++ b/umbraco/cms/businesslogic/datatype/DataTypeDefinition.cs @@ -4,6 +4,7 @@ using System.Data; using System.Collections; using umbraco.DataLayer; using System.Xml; +using umbraco.interfaces; namespace umbraco.cms.businesslogic.datatype { @@ -17,20 +18,52 @@ namespace umbraco.cms.businesslogic.datatype /// public class DataTypeDefinition : CMSNode { - private Guid _controlId; - - private static Guid _objectType = new Guid("30a2a501-1978-4ddb-a57b-f7efed43ba3c"); + #region Private fields + private Guid _controlId; + private static Guid _objectType = new Guid("30a2a501-1978-4ddb-a57b-f7efed43ba3c"); + #endregion - /// - /// Initialization of the datatypedefinition - /// - /// Datattypedefininition id - public DataTypeDefinition(int id) : base(id) - { - setupDataTypeDefinition(); - } + #region Constructors + /// + /// Initialization of the datatypedefinition + /// + /// Datattypedefininition id + public DataTypeDefinition(int id) : base(id) { } + + /// + /// Initialization of the datatypedefinition + /// + /// Datattypedefininition id + public DataTypeDefinition(Guid id) : base(id) { } + + #endregion + + #region Public Properties + /// + /// The associated datatype, which delivers the methods for editing data, editing prevalues see: umbraco.interfaces.IDataType + /// + public IDataType DataType + { + get + { + cms.businesslogic.datatype.controls.Factory f = new cms.businesslogic.datatype.controls.Factory(); + interfaces.IDataType dt = f.DataType(_controlId); + dt.DataTypeDefinitionId = Id; + + return dt; + } + set + { + SqlHelper.ExecuteNonQuery("update cmsDataType set controlId = @id where nodeID = " + this.Id.ToString(), + SqlHelper.CreateParameter("@id", value.Id)); + _controlId = value.Id; + } + } + #endregion + + #region Public methods public override void delete() { //delete the cmsDataType role, then the umbracoNode @@ -46,16 +79,6 @@ namespace umbraco.cms.businesslogic.datatype delete(); } - /// - /// Initialization of the datatypedefinition - /// - /// Datattypedefininition id - public DataTypeDefinition(Guid id) : base(id) - { - setupDataTypeDefinition(); - } - - /// /// Used to persist object changes to the database. In Version3.0 it's just a stub for future compatibility /// @@ -64,63 +87,49 @@ namespace umbraco.cms.businesslogic.datatype OnSaving(EventArgs.Empty); } - - - private void setupDataTypeDefinition() { - IRecordsReader dr = SqlHelper.ExecuteReader( "select dbType, controlId from cmsDataType where nodeId = '" + this.Id.ToString() + "'"); - if (dr.Read()) - { - _controlId = dr.GetGuid("controlId"); - } - else - throw new ArgumentException("No dataType with id = " + this.Id.ToString() + " found"); - dr.Close(); - } - - - /// - /// The associated datatype, which delivers the methods for editing data, editing prevalues see: umbraco.interfaces.IDataType - /// - public interfaces.IDataType DataType - { + /* + public SortedList PreValues { get { - cms.businesslogic.datatype.controls.Factory f = new cms.businesslogic.datatype.controls.Factory(); - interfaces.IDataType dt = f.DataType(_controlId); - dt.DataTypeDefinitionId = Id; - - return dt; + SortedList retVal = new SortedList(); + SqlDataReader dr = SqlHelper.ExecuteReader("select id, value from cmsDataTypePreValues where dataTypeNodeId = @nodeId order by sortOrder", SqlHelper.CreateParameter("@nodeId", this.Id)); + while (dr.Read()) + { + retVal.Add(dr.GetString("id"), dr.GetString("value")); + } + dr.Close(); + + return retVal; } - set - { - SqlHelper.ExecuteNonQuery( "update cmsDataType set controlId = @id where nodeID = " + this.Id.ToString(), - SqlHelper.CreateParameter("@id",value.Id)); - _controlId = value.Id; - } } + */ - - public XmlElement ToXml(XmlDocument xd) { + public XmlElement ToXml(XmlDocument xd) + { XmlElement dt = xd.CreateElement("DataType"); dt.Attributes.Append(xmlHelper.addAttribute(xd, "Name", Text)); dt.Attributes.Append(xmlHelper.addAttribute(xd, "Id", this.DataType.Id.ToString())); dt.Attributes.Append(xmlHelper.addAttribute(xd, "Definition", this.UniqueId.ToString())); - + // templates XmlElement prevalues = xd.CreateElement("PreValues"); - foreach (DictionaryEntry item in PreValues.GetPreValues(this.Id)) { + foreach (DictionaryEntry item in PreValues.GetPreValues(this.Id)) + { XmlElement prevalue = xd.CreateElement("PreValue"); - prevalue.Attributes.Append(xmlHelper.addAttribute(xd, "Id", ((umbraco.cms.businesslogic.datatype.PreValue) item.Value).Id.ToString() )); - prevalue.Attributes.Append(xmlHelper.addAttribute(xd, "Value", ((umbraco.cms.businesslogic.datatype.PreValue) item.Value).Value )); + prevalue.Attributes.Append(xmlHelper.addAttribute(xd, "Id", ((umbraco.cms.businesslogic.datatype.PreValue)item.Value).Id.ToString())); + prevalue.Attributes.Append(xmlHelper.addAttribute(xd, "Value", ((umbraco.cms.businesslogic.datatype.PreValue)item.Value).Value)); prevalues.AppendChild(prevalue); } dt.AppendChild(prevalues); - - return dt; - } - public static DataTypeDefinition Import(XmlNode xmlData) { + return dt; + } + #endregion + + #region Static methods + public static DataTypeDefinition Import(XmlNode xmlData) + { string _name = xmlData.Attributes["Name"].Value; string _id = xmlData.Attributes["Id"].Value; string _def = xmlData.Attributes["Definition"].Value; @@ -128,12 +137,13 @@ namespace umbraco.cms.businesslogic.datatype //Make sure that the dtd is not already present if (!CMSNode.IsNode(new Guid(_def)) - ) { - + ) + { + BasePages.UmbracoEnsuredPage uep = new umbraco.BasePages.UmbracoEnsuredPage(); BusinessLogic.User u = uep.getUser(); - - if(u == null) + + if (u == null) u = BusinessLogic.User.GetUser(0); cms.businesslogic.datatype.controls.Factory f = new umbraco.cms.businesslogic.datatype.controls.Factory(); @@ -150,70 +160,55 @@ namespace umbraco.cms.businesslogic.datatype XmlAttribute val = xmlPv.Attributes["Value"]; - if (val != null && !string.IsNullOrEmpty(val.Value)) { + if (val != null && !string.IsNullOrEmpty(val.Value)) + { PreValue p = new PreValue(0, 0, val.Value); p.DataTypeId = dtd.Id; p.Save(); } } - return dtd; + return dtd; } return null; } - - /* - public SortedList PreValues { - get { - SortedList retVal = new SortedList(); - SqlDataReader dr = SqlHelper.ExecuteReader("select id, value from cmsDataTypePreValues where dataTypeNodeId = @nodeId order by sortOrder", SqlHelper.CreateParameter("@nodeId", this.Id)); - while (dr.Read()) - { - retVal.Add(dr.GetString("id"), dr.GetString("value")); - } - dr.Close(); - - return retVal; - } - } - */ - - /// - /// Retrieves a list of all datatypedefinitions - /// - /// A list of all datatypedefinitions - public static DataTypeDefinition[] GetAll() - { - SortedList retvalSort = new SortedList(); + /// + /// Retrieves a list of all datatypedefinitions + /// + /// A list of all datatypedefinitions + public static DataTypeDefinition[] GetAll() + { + SortedList retvalSort = new SortedList(); Guid[] tmp = CMSNode.getAllUniquesFromObjectType(_objectType); - DataTypeDefinition[] retval = new DataTypeDefinition[tmp.Length]; - for(int i = 0; i < tmp.Length; i++) { - DataTypeDefinition dt = DataTypeDefinition.GetDataTypeDefinition(tmp[i]); - retvalSort.Add(dt.Text + "|||" + Guid.NewGuid().ToString(), dt); - } + DataTypeDefinition[] retval = new DataTypeDefinition[tmp.Length]; + for (int i = 0; i < tmp.Length; i++) + { + DataTypeDefinition dt = DataTypeDefinition.GetDataTypeDefinition(tmp[i]); + retvalSort.Add(dt.Text + "|||" + Guid.NewGuid().ToString(), dt); + } - IDictionaryEnumerator ide = retvalSort.GetEnumerator(); - int counter = 0; - while (ide.MoveNext()) - { - retval[counter] = (DataTypeDefinition) ide.Value; - counter++; - } - return retval; - } + IDictionaryEnumerator ide = retvalSort.GetEnumerator(); + int counter = 0; + while (ide.MoveNext()) + { + retval[counter] = (DataTypeDefinition)ide.Value; + counter++; + } + return retval; + } - /// - /// Creates a new datatypedefinition given its name and the user which creates it. - /// - /// The user who creates the datatypedefinition - /// The name of the DataTypeDefinition - /// - public static DataTypeDefinition MakeNew(BusinessLogic.User u, string Text) - { + /// + /// Creates a new datatypedefinition given its name and the user which creates it. + /// + /// The user who creates the datatypedefinition + /// The name of the DataTypeDefinition + /// + public static DataTypeDefinition MakeNew(BusinessLogic.User u, string Text) + { return MakeNew(u, Text, Guid.NewGuid()); - } + } /// /// Creates a new datatypedefinition given its name and the user which creates it. @@ -222,7 +217,8 @@ namespace umbraco.cms.businesslogic.datatype /// The name of the DataTypeDefinition /// Overrides the CMSnodes uniqueID /// - public static DataTypeDefinition MakeNew(BusinessLogic.User u, string Text, Guid UniqueId) { + public static DataTypeDefinition MakeNew(BusinessLogic.User u, string Text, Guid UniqueId) + { int newId = CMSNode.MakeNew(-1, _objectType, u.Id, 1, Text, UniqueId).Id; cms.businesslogic.datatype.controls.Factory f = new cms.businesslogic.datatype.controls.Factory(); @@ -233,29 +229,29 @@ namespace umbraco.cms.businesslogic.datatype DataTypeDefinition dtd = new DataTypeDefinition(newId); dtd.OnNew(EventArgs.Empty); - return dtd; + return dtd; } - /// - /// Retrieve a list of datatypedefinitions which share the same IDataType datatype - /// - /// The unique id of the IDataType - /// A list of datatypedefinitions which are based on the IDataType specified - public static DataTypeDefinition GetByDataTypeId(Guid DataTypeId) - { - int dfId = 0; - foreach(DataTypeDefinition df in DataTypeDefinition.GetAll()) - if (df.DataType.Id == DataTypeId) - { - dfId = df.Id; - break; - } + /// + /// Retrieve a list of datatypedefinitions which share the same IDataType datatype + /// + /// The unique id of the IDataType + /// A list of datatypedefinitions which are based on the IDataType specified + public static DataTypeDefinition GetByDataTypeId(Guid DataTypeId) + { + int dfId = 0; + foreach (DataTypeDefinition df in DataTypeDefinition.GetAll()) + if (df.DataType.Id == DataTypeId) + { + dfId = df.Id; + break; + } - if (dfId == 0) - return null; - else - return new DataTypeDefinition(dfId); - } + if (dfId == 0) + return null; + else + return new DataTypeDefinition(dfId); + } /// /// Analyzes an object to see if its basetype is umbraco.editorControls.DefaultData @@ -291,9 +287,26 @@ namespace umbraco.cms.businesslogic.datatype System.Web.HttpRuntime.Cache.Insert(string.Format("UmbracoDataTypeDefinition{0}", id.ToString()), dt); } return (DataTypeDefinition)System.Web.HttpRuntime.Cache[string.Format("UmbracoDataTypeDefinition{0}", id.ToString())]; - } + } + #endregion + #region Protected methods + protected override void setupNode() + { + base.setupNode(); + IRecordsReader dr = SqlHelper.ExecuteReader("select dbType, controlId from cmsDataType where nodeId = '" + this.Id.ToString() + "'"); + if (dr.Read()) + { + _controlId = dr.GetGuid("controlId"); + } + else + throw new ArgumentException("No dataType with id = " + this.Id.ToString() + " found"); + dr.Close(); + } + #endregion + + #region Events //EVENTS public delegate void SaveEventHandler(DataTypeDefinition sender, EventArgs e); public delegate void NewEventHandler(DataTypeDefinition sender, EventArgs e); @@ -303,22 +316,26 @@ namespace umbraco.cms.businesslogic.datatype /// Occurs when a macro is saved. /// public static event SaveEventHandler Saving; - protected virtual void OnSaving(EventArgs e) { + protected virtual void OnSaving(EventArgs e) + { if (Saving != null) Saving(this, e); } public static event NewEventHandler New; - protected virtual void OnNew(EventArgs e) { + protected virtual void OnNew(EventArgs e) + { if (New != null) New(this, e); } public static event DeleteEventHandler Deleting; - protected virtual void OnDeleting(EventArgs e) { + protected virtual void OnDeleting(EventArgs e) + { if (Deleting != null) Deleting(this, e); - } + } + #endregion } } \ No newline at end of file diff --git a/umbraco/cms/businesslogic/media/Media.cs b/umbraco/cms/businesslogic/media/Media.cs index 61c4f8fd93..a1a2e1f03d 100644 --- a/umbraco/cms/businesslogic/media/Media.cs +++ b/umbraco/cms/businesslogic/media/Media.cs @@ -203,10 +203,11 @@ namespace umbraco.cms.businesslogic.media } } } - /// + + /// /// Deletes the current media and all children. /// - new public void delete() + public override void delete() { // Check for recyle bin if (!Path.Contains("," + ((int)RecycleBin.RecycleBinType.Media).ToString() + ",")) @@ -278,7 +279,6 @@ namespace umbraco.cms.businesslogic.media } - //EVENTS /// /// The save event handler diff --git a/umbraco/cms/businesslogic/web/Document.cs b/umbraco/cms/businesslogic/web/Document.cs index 51ac3cb0ed..424c406504 100644 --- a/umbraco/cms/businesslogic/web/Document.cs +++ b/umbraco/cms/businesslogic/web/Document.cs @@ -1,25 +1,15 @@ -using System.Web.UI.WebControls; -using umbraco.cms.businesslogic.template; -using System.Web.UI; - using System; using System.Collections; -using System.Data; +using System.Collections.Generic; +using System.Linq; using System.Web; using System.Xml; - using umbraco.BusinessLogic; using umbraco.BusinessLogic.Actions; -using umbraco.BusinessLogic.console; - using umbraco.cms.businesslogic.property; using umbraco.cms.businesslogic.relation; using umbraco.cms.helpers; using umbraco.DataLayer; -using System.Collections.Generic; -using umbraco.cms.businesslogic.propertytype; -using System.Linq; -using System.ComponentModel; using umbraco.IO; namespace umbraco.cms.businesslogic.web @@ -143,7 +133,7 @@ namespace umbraco.cms.businesslogic.web } #endregion - #region Constants + #region Constants and Static members private const string m_SQLOptimizedSingle = @" Select (select count(id) from umbracoNode where parentId = @id) as Children, @@ -200,11 +190,14 @@ namespace umbraco.cms.businesslogic.web inner join cmsPreviewXml on cmsPreviewXml.nodeId = cmsDocument.nodeId and cmsPreviewXml.versionId = cmsDocument.versionId where newest = 1 and trashed = 0 and path like '{0}' order by level,sortOrder - "; + "; + + public static Guid _objectType = new Guid("c66ba18e-eaf3-4cff-8a22-41b16d66a972"); + #endregion #region Private properties - public static Guid _objectType = new Guid("c66ba18e-eaf3-4cff-8a22-41b16d66a972"); + private DateTime _updated; private DateTime _release; private DateTime _expire; @@ -548,10 +541,9 @@ namespace umbraco.cms.businesslogic.web set { - // Esben Carlsen: ????? value never used ?? --> needs update - _published = false; + _published = value; SqlHelper.ExecuteNonQuery( - string.Format("update cmsDocument set published = 0 where nodeId = {0}", Id)); + string.Format("update cmsDocument set published = {0} where nodeId = {1}", Id, value ? 1 : 0)); } } @@ -564,6 +556,9 @@ namespace umbraco.cms.businesslogic.web if (!e.Cancel) { SqlHelper.ExecuteNonQuery(string.Format("update cmsDocument set published = 0 where nodeId = {0}", Id)); + + _published = false; + FireAfterUnPublish(e); } } @@ -588,6 +583,8 @@ namespace umbraco.cms.businesslogic.web // } //} + UpdateDate = DateTime.Now; //set the updated date to now + base.Save(); // update preview xml SaveXmlPreview(new XmlDocument()); @@ -626,6 +623,17 @@ namespace umbraco.cms.businesslogic.web return (SqlHelper.ExecuteScalar("select Count(published) as tmp from cmsDocument where published = 1 And nodeId =" + Id) > 0); } + /// + /// Pending changes means that there have been property/data changes since the last published version. + /// This is determined by the comparing the version date to the updated date. if they are different by .5 seconds, + /// then this is considered a change. + /// + /// + public bool HasPendingChanges() + { + return new TimeSpan(UpdateDate.Ticks - VersionDate.Ticks).TotalMilliseconds > 500; + } + protected void InitializeDocument(User InitUser, User InitWriter, string InitText, int InitTemplate, DateTime InitReleaseDate, DateTime InitExpireDate, DateTime InitUpdateDate, bool InitPublished) @@ -976,66 +984,93 @@ namespace umbraco.cms.businesslogic.web /// /// Deletes the current document (and all children recursive) /// - public new void delete() + public override void delete() { // Check for recyle bin if (!Path.Contains("," + ((int)RecycleBin.RecycleBinType.Content).ToString() + ",")) { - MoveToTrashEventArgs e = new MoveToTrashEventArgs(); - FireBeforeMoveToTrash(e); - - if (!e.Cancel) - { - umbraco.BusinessLogic.Actions.Action.RunActionHandlers(this, ActionDelete.Instance); - UnPublish(); - Move((int)RecycleBin.RecycleBinType.Content); - - - FireAfterMoveToTrash(e); - } - + MoveToTrash(); } else { - - DeleteEventArgs e = new DeleteEventArgs(); - - FireBeforeDelete(e); - - if (!e.Cancel) - { - - //store children array here because iterating over an Array object is very inneficient. - var c = Children; - foreach (Document d in c) - { - d.delete(); - } - - umbraco.BusinessLogic.Actions.Action.RunActionHandlers(this, ActionDelete.Instance); - - //delete files - interfaces.IDataType uploadField = new cms.businesslogic.datatype.controls.Factory().GetNewObject(new Guid("5032a6e6-69e3-491d-bb28-cd31cd11086c")); - var props = this.getProperties; - foreach (cms.businesslogic.property.Property p in props) - - if (p.PropertyType.DataTypeDefinition.DataType.Id == uploadField.Id && - p.Value.ToString() != "" && - System.IO.File.Exists(IOHelper.MapPath(p.Value.ToString())) - ) - System.IO.File.Delete(IOHelper.MapPath(p.Value.ToString())); - - - SqlHelper.ExecuteNonQuery("delete from cmsDocument where NodeId = " + Id); - HttpContext.Current.Trace.Write("documentdelete", "base delete"); - base.delete(); - HttpContext.Current.Trace.Write("documentdelete", "after base delete"); - - FireAfterDelete(e); - } + DeletePermanently(false); } } + /// + /// Used internally to permanently delete the data from the database + /// + /// + /// if onlyCurrentDocType is set, this means that we shouldn't delete any children that are not + /// the current document type and instead just move them to the recycle bin. This is effective if + /// we're deleting an entire document type but don't want to delete other data that isn't this document type + /// but the ndoe exists as a child of the document type that is being deleted. + /// + /// returns true if deletion isn't cancelled + private bool DeletePermanently(bool onlyCurrentDocType) + { + DeleteEventArgs e = new DeleteEventArgs(); + + FireBeforeDelete(e); + + if (!e.Cancel) + { + + var c = Children; + foreach (Document d in c) + { + if (onlyCurrentDocType && (d.ContentType.Id != this.ContentType.Id)) + { + d.MoveToTrash(); + } + else + { + d.DeletePermanently(onlyCurrentDocType); + } + } + + umbraco.BusinessLogic.Actions.Action.RunActionHandlers(this, ActionDelete.Instance); + + //delete files + interfaces.IDataType uploadField = new cms.businesslogic.datatype.controls.Factory().GetNewObject(new Guid("5032a6e6-69e3-491d-bb28-cd31cd11086c")); + var props = this.getProperties; + foreach (Property p in props) + { + if (p.PropertyType.DataTypeDefinition.DataType.Id == uploadField.Id && + p.Value.ToString() != "" && + System.IO.File.Exists(IOHelper.MapPath(p.Value.ToString()))) + { + System.IO.File.Delete(IOHelper.MapPath(p.Value.ToString())); + } + } + + SqlHelper.ExecuteNonQuery("delete from cmsDocument where NodeId = " + Id); + base.delete(); + + FireAfterDelete(e); + } + return !e.Cancel; + } + + /// + /// Used internally to move the node to the recyle bin + /// + /// Returns true if the move was not cancelled + private bool MoveToTrash() + { + MoveToTrashEventArgs e = new MoveToTrashEventArgs(); + FireBeforeMoveToTrash(e); + + if (!e.Cancel) + { + umbraco.BusinessLogic.Actions.Action.RunActionHandlers(this, ActionDelete.Instance); + UnPublish(); + Move((int)RecycleBin.RecycleBinType.Content); + FireAfterMoveToTrash(e); + } + return !e.Cancel; + } + /// /// Deletes all documents of a type, will be invoked if a documenttype is deleted. /// @@ -1050,8 +1085,8 @@ namespace umbraco.cms.businesslogic.web // due to recursive structure document might already been deleted.. if (IsNode(c.UniqueId)) { - Document tmp = new Document(c.UniqueId); - tmp.delete(); + Document d = new Document(c.UniqueId); + d.DeletePermanently(true); } } } @@ -1804,62 +1839,4 @@ namespace umbraco.cms.businesslogic.web } - - /// - /// A lightweight datastructure used to represent a version of a document - /// - public class DocumentVersionList - { - private Guid _version; - private DateTime _date; - private string _text; - private User _user; - - /// - /// The unique id of the version - /// - public Guid Version - { - get { return _version; } - } - - /// - /// The date of the creation of the version - /// - public DateTime Date - { - get { return _date; } - } - - /// - /// The name of the document in the version - /// - public string Text - { - get { return _text; } - } - - /// - /// The user which created the version - /// - public User User - { - get { return _user; } - } - - /// - /// Initializes a new instance of the DocumentVersionList class. - /// - /// Unique version id - /// Version createdate - /// Version name - /// Creator - public DocumentVersionList(Guid Version, DateTime Date, string Text, User User) - { - _version = Version; - _date = Date; - _text = Text; - _user = User; - } - } } diff --git a/umbraco/cms/businesslogic/web/DocumentType.cs b/umbraco/cms/businesslogic/web/DocumentType.cs index 251ee0c580..add0d51285 100644 --- a/umbraco/cms/businesslogic/web/DocumentType.cs +++ b/umbraco/cms/businesslogic/web/DocumentType.cs @@ -30,9 +30,11 @@ namespace umbraco.cms.businesslogic.web #endregion - #region Private Members + #region Constants and Static members + public static Guid _objectType = new Guid("a2cb7800-f571-4787-9638-bc48539a0efb"); + #endregion - private static Guid _objectType = new Guid("a2cb7800-f571-4787-9638-bc48539a0efb"); + #region Private Members private ArrayList _templateIds = new ArrayList(); private int _defaultTemplate; @@ -222,6 +224,7 @@ namespace umbraco.cms.businesslogic.web { if (dt.MasterContentType == this.Id) { + //this should be InvalidOperationException (or something other than ArgumentException)! throw new ArgumentException("Can't delete a Document Type used as a Master Content Type. Please remove all references first!"); } } diff --git a/umbraco/cms/businesslogic/web/DocumentVersionList.cs b/umbraco/cms/businesslogic/web/DocumentVersionList.cs new file mode 100644 index 0000000000..5709d71295 --- /dev/null +++ b/umbraco/cms/businesslogic/web/DocumentVersionList.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Web; +using System.Xml; +using umbraco.BusinessLogic; +using umbraco.BusinessLogic.Actions; +using umbraco.cms.businesslogic.property; +using umbraco.cms.businesslogic.relation; +using umbraco.cms.helpers; +using umbraco.DataLayer; +using umbraco.IO; + +namespace umbraco.cms.businesslogic.web +{ + /// + /// A lightweight datastructure used to represent a version of a document + /// + public class DocumentVersionList + { + private Guid _version; + private DateTime _date; + private string _text; + private User _user; + + /// + /// The unique id of the version + /// + public Guid Version + { + get { return _version; } + } + + /// + /// The date of the creation of the version + /// + public DateTime Date + { + get { return _date; } + } + + /// + /// The name of the document in the version + /// + public string Text + { + get { return _text; } + } + + /// + /// The user which created the version + /// + public User User + { + get { return _user; } + } + + /// + /// Initializes a new instance of the DocumentVersionList class. + /// + /// Unique version id + /// Version createdate + /// Version name + /// Creator + public DocumentVersionList(Guid Version, DateTime Date, string Text, User User) + { + _version = Version; + _date = Date; + _text = Text; + _user = User; + } + } +} diff --git a/umbraco/cms/umbraco.cms.csproj b/umbraco/cms/umbraco.cms.csproj index 9023d983ec..5c90c07ef5 100644 --- a/umbraco/cms/umbraco.cms.csproj +++ b/umbraco/cms/umbraco.cms.csproj @@ -181,6 +181,7 @@ + Code @@ -208,7 +209,7 @@ - + Code diff --git a/umbraco/presentation/umbraco/Trees/BaseContentTree.cs b/umbraco/presentation/umbraco/Trees/BaseContentTree.cs index bd52b3f9f1..0f5d156afa 100644 --- a/umbraco/presentation/umbraco/Trees/BaseContentTree.cs +++ b/umbraco/presentation/umbraco/Trees/BaseContentTree.cs @@ -190,8 +190,9 @@ function openContent(id) { treeElement.NotPublished = false; if (dd.Published) { - if (Math.Round(new TimeSpan(dd.UpdateDate.Ticks - dd.VersionDate.Ticks).TotalSeconds, 0) > 1) - treeElement.NotPublished = true; + //if (Math.Round(new TimeSpan(dd.UpdateDate.Ticks - dd.VersionDate.Ticks).TotalSeconds, 0) > 1) + // treeElement.NotPublished = true; + treeElement.NotPublished = dd.HasPendingChanges(); } else treeElement.NotPublished = true; diff --git a/umbraco/presentation/umbraco/editContent.aspx.cs b/umbraco/presentation/umbraco/editContent.aspx.cs index e8e477264e..cde17f3a22 100644 --- a/umbraco/presentation/umbraco/editContent.aspx.cs +++ b/umbraco/presentation/umbraco/editContent.aspx.cs @@ -250,9 +250,7 @@ namespace umbraco.cms.presentation //newName.Text = _document.Text; } - // Update the update date - _document.UpdateDate = System.DateTime.Now; - dp.Text = _document.UpdateDate.ToShortDateString() + " " + _document.UpdateDate.ToShortTimeString(); + if (dpRelease.DateTime > new DateTime(1753, 1, 1) && dpRelease.DateTime < new DateTime(9999, 12, 31)) _document.ReleaseDate = dpRelease.DateTime; else @@ -272,6 +270,9 @@ namespace umbraco.cms.presentation BusinessLogic.Actions.Action.RunActionHandlers(_document, ActionUpdate.Instance); _document.Save(); + // Update the update date + dp.Text = _document.UpdateDate.ToShortDateString() + " " + _document.UpdateDate.ToShortTimeString(); + if (!tmp.DoesPublish) ClientTools.ShowSpeechBubble(speechBubbleIcon.save, ui.Text("speechBubbles", "editContentSavedHeader", null), ui.Text("speechBubbles", "editContentSavedText", null)); diff --git a/umbraco/presentation/umbraco/webservices/trashcan.asmx.cs b/umbraco/presentation/umbraco/webservices/trashcan.asmx.cs index 7e9c489482..e7a39a9ead 100644 --- a/umbraco/presentation/umbraco/webservices/trashcan.asmx.cs +++ b/umbraco/presentation/umbraco/webservices/trashcan.asmx.cs @@ -53,27 +53,16 @@ namespace umbraco.presentation.webservices private void emptyTrashCanDo(cms.businesslogic.RecycleBin.RecycleBinType type) { RecycleBin trashCan = new RecycleBin(type); - //store children array here because iterating over an Array property object is very inneficient. - var c = trashCan.Children; - foreach (IconI d in c) + + var callback = new Action(x => { - // we need this check as we can't move the responsibility to an interface - if (type == RecycleBin.RecycleBinType.Content) - { - new Document(d.Id).delete(); - } - else if (type == RecycleBin.RecycleBinType.Media) - { - new Media(d.Id).delete(); - } - else - { - throw new ArgumentException(String.Format("The type {0} is unknown in the emptyTrashCanDo method", type)); - } Application.Lock(); - Application["trashcanEmptyLeft"] = RecycleBin.Count(type).ToString(); + Application["trashcanEmptyLeft"] = x.ToString(); Application.UnLock(); - } + }); + + trashCan.CallTheGarbageMan(callback); + } } }