/// <summary>
        /// Ensure that all resources are of the required type.
        /// </summary>
        internal static void VerifyResourceTypes(ReportTemplateSettings reportSettings, ReadiNow.Model.ReportTemplate reportTemplate)
        {
            EntityType appliesToType = reportTemplate.ReportTemplateAppliesToType;

            if (appliesToType != null)
            {
                if (reportSettings.SelectedResources == null || reportSettings.SelectedResources.Count == 0)
                {
                    throw new Exception("The report template must apply to a resource, but no resource was provided. ");
                }
                else
                {
                    foreach (EntityRef resourceId in reportSettings.SelectedResources)
                    {
                        IEntity entity = Entity.Get(resourceId);
                        if (!PerTenantEntityTypeCache.Instance.IsInstanceOf(entity, appliesToType.Id))
                        {
                            throw new Exception(string.Format("The selected resource is not of the required type for this document. Expected type '{0}', but received '{1}' of type '{2}.'",
                                                              appliesToType.Name, Entity.GetName(resourceId.Id), Entity.GetName(entity.TypeIds.First( ))));
                        }
                    }
                }
            }
        }
        /// <summary>
        ///     Worker thread method to generate a report document.
        /// </summary>
        /// <param name="reportSettings">The report-template settings.</param>
        /// <param name="taskInfo">The long-running task info object.</param>
        private static void GenerateDocumentWorker(ReportTemplateSettings reportSettings, LongRunningInfo taskInfo)
        {
            // Load report template resource
            var reportTemplate = Entity.Get <ReadiNow.Model.ReportTemplate>(reportSettings.ReportTemplate);

            if (reportTemplate == null)
            {
                throw new Exception("Report template could not be loaded.");
            }

            // Load the document resource
            if (reportTemplate.ReportTemplateUsesDocument == null)
            {
                throw new Exception("Report template has no associated template document.");
            }

            long docId = reportTemplate.ReportTemplateUsesDocument.Id;

            // Load the document stream
            RevisionData templateData   = GetLatestDocument(docId);
            Stream       templateStream = templateData.Stream;
            string       extension      = templateData.FileExtension;

            // Verify supported types
            if (extension != ".docx" && extension != ".dotx" && extension != ".docm" && extension != ".dotm")
            {
                throw new Exception("Report template has an unsupported file extension.");
            }

            // Verify resource types
            VerifyResourceTypes(reportSettings, reportTemplate);

            // Fill settings
            var settings = new GeneratorSettings
            {
                TimeZoneName = reportSettings.TimeZone
            };

            if (reportSettings.SelectedResources != null)
            {
                settings.SelectedResourceId = reportSettings.SelectedResources.First( ).Id;
            }

            // Generate the document
            var outputSteam = new MemoryStream( );

            Factory.DocumentGenerator.CreateDocument(templateStream, outputSteam, settings);
            outputSteam.Flush( );
            outputSteam.Position = 0;

            // Store file stream in the temporary table
            string token = PersistTemporaryFile(outputSteam);

            // Store the result into a document entity
            string filename   = string.Format("{0} {1:s}", reportTemplate.Name, DateTime.Now);
            long   documentId = StoreFileInEntity(outputSteam, filename, extension, token);

            // Return token that is the document identifier that is used for the download of the document file entity.
            taskInfo.ResultData = documentId.ToString(CultureInfo.InvariantCulture);
            LongRunningHelper.SaveLongRunningTaskInfo(taskInfo);
        }
        /// <summary>
        ///     Start generating a document, combining report data and a given report template.
        /// </summary>
        /// <param name="settings">Settings used for the document generation.</param>
        /// <returns>An identifier or token that can be used to interact with the long running task.</returns>
        public string GenerateReportFromTemplate(ReportTemplateSettings settings)
        {
            LongRunningInfo taskInfo = LongRunningHelper.StartLongRunningInWorkerThread(info => GenerateDocumentWorker(settings, info));

            return(taskInfo.TaskId.ToString(@"N"));
        }