<commit_before>﻿using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Xml.Linq;
using Chauffeur.Host;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Services;
using Task = System.Threading.Tasks.Task;

namespace Chauffeur.Deliverables
{
    [DeliverableName("content-type")]
    [DeliverableAlias("ct")]
    public sealed class ContentTypeDeliverable : Deliverable, IProvideDirections
    {
        private readonly IContentTypeService contentTypeService;
        private readonly IDatabaseUnitOfWorkProvider uowProvider;
        private readonly IPackagingService packagingService;

        public ContentTypeDeliverable(
            TextReader reader,
            TextWriter writer,
            IContentTypeService contentTypeService,
            IDatabaseUnitOfWorkProvider uowProvider,
            IPackagingService packagingService
            )
            : base(reader, writer)
        {
            this.contentTypeService = contentTypeService;
            this.uowProvider = uowProvider;
            this.packagingService = packagingService;
        }

        public override async Task<DeliverableResponse> Run(string command, string[] args)
        {
            var operation = args[0];

            switch (operation.ToLower())
            {
                case "get-all":
                    await GetAll();
                    break;

                case "get":
                    await Get(args.Skip(1).ToArray());
                    break;

                case "export":
                    await Export(args.Skip(1).ToArray());
                    break;

                case "import":
                    await Import(args.Skip(1).ToArray());
                    break;

                default:
                    await Out.WriteLineFormattedAsync("The operation `{0}` is not supported", operation);
                    break;
            }

            return await base.Run(command, args);
        }

        private async Task Import(string[] args)
        {
            if (!args.Any())
            {
                await Out.WriteLineAsync("No import target defined");
                return;
            }

            var deliveryName = args[0].Trim();

            string directory;
            if (!UmbracoHost.Current.Settings.TryGetChauffeurDirectory(out directory))
                return;

            var file = Path.Combine(directory, deliveryName + ".xml");
            if (!System.IO.File.Exists(file))
            {
                await Out.WriteLineFormattedAsync("Unable to located the import script '{0}'", deliveryName);
                return;
            }

            var xml = XDocument.Load(file);

            packagingService.ImportContentTypes(xml.Elements().First());

            await Out.WriteLineFormattedAsync("Content Type has been imported");
        }

        private async Task Export(string[] args)
        {
            var contentType = await Get(args, false);

            if (contentType == null)
                return;

            string exportDirectory;
            if (!UmbracoHost.Current.Settings.TryGetChauffeurDirectory(out exportDirectory))
                return;

            var xml = new XDocument();

            xml.Add(packagingService.Export(contentType, false));

            var fileName = DateTime.UtcNow.ToString("yyyyMMdd") + "-" + contentType.Alias + ".xml";
            xml.Save(Path.Combine(exportDirectory, fileName));
            await Out.WriteLineFormattedAsync("Content Type has been exported with file name '{0}'", fileName);
        }

        private async Task<IContentType> Get(string[] args, bool dump = true)
        {
            if (!args.Any())
            {
                await Out.WriteLineAsync("Please provide the numerical id or alias if the doc type to get");
                return null;
            }

            IContentType contentType = null;
            var foundWith = "id";
            int id;
            if (int.TryParse(args[0], out id))
                contentType = contentTypeService.GetContentType(id);
            else
            {
                //Sigh, can't use the GetByAlias because at the moment there are internal dependencies I can't load
                //contentType = cts.GetContentType(args[0]);

                //instead we'll find the ID ourselves from the DB
                var sql = new Sql()
                    .Select("NodeId")
                   .From("cmsContentType")
                   .Where("alias = @0", new[] { args[0] })
                   ;
                var uow = uowProvider.GetUnitOfWork();
                var ids = uow.Database.Fetch<int>(sql);
                if (ids.Any())
                    contentType = contentTypeService.GetContentType(ids.First());

                foundWith = "alias";
            }

            if (contentType == null)
            {
                await Out.WriteLineFormattedAsync("No content type found with {0} of '{1}'", foundWith, args[0]);
                return null;
            }

            // I'm sorry about the following code. In Umbraco 7.0.0 PropertyType.PropertyGroupId is:
            //  a) Not set when you do GetContentType
            //  b) An internal property (!!)
            // So to ensure that the object is proprely constructed I have to use reflection and SQL
            // and set it up myself!<commit_msg>Can now use alias lookup with because of the other hacks introduced for mappers<commit_after>﻿using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Xml.Linq;
using Chauffeur.Host;
using Umbraco.Core.Models;
using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.UnitOfWork;
using Umbraco.Core.Services;
using Task = System.Threading.Tasks.Task;

namespace Chauffeur.Deliverables
{
    [DeliverableName("content-type")]
    [DeliverableAlias("ct")]
    public sealed class ContentTypeDeliverable : Deliverable, IProvideDirections
    {
        private readonly IContentTypeService contentTypeService;
        private readonly IDatabaseUnitOfWorkProvider uowProvider;
        private readonly IPackagingService packagingService;

        public ContentTypeDeliverable(
            TextReader reader,
            TextWriter writer,
            IContentTypeService contentTypeService,
            IDatabaseUnitOfWorkProvider uowProvider,
            IPackagingService packagingService
            )
            : base(reader, writer)
        {
            this.contentTypeService = contentTypeService;
            this.uowProvider = uowProvider;
            this.packagingService = packagingService;
        }

        public override async Task<DeliverableResponse> Run(string command, string[] args)
        {
            var operation = args[0];

            switch (operation.ToLower())
            {
                case "get-all":
                    await GetAll();
                    break;

                case "get":
                    await Get(args.Skip(1).ToArray());
                    break;

                case "export":
                    await Export(args.Skip(1).ToArray());
                    break;

                case "import":
                    await Import(args.Skip(1).ToArray());
                    break;

                default:
                    await Out.WriteLineFormattedAsync("The operation `{0}` is not supported", operation);
                    break;
            }

            return await base.Run(command, args);
        }

        private async Task Import(string[] args)
        {
            if (!args.Any())
            {
                await Out.WriteLineAsync("No import target defined");
                return;
            }

            var deliveryName = args[0].Trim();

            string directory;
            if (!UmbracoHost.Current.Settings.TryGetChauffeurDirectory(out directory))
                return;

            var file = Path.Combine(directory, deliveryName + ".xml");
            if (!System.IO.File.Exists(file))
            {
                await Out.WriteLineFormattedAsync("Unable to located the import script '{0}'", deliveryName);
                return;
            }

            var xml = XDocument.Load(file);

            packagingService.ImportContentTypes(xml.Elements().First());

            await Out.WriteLineFormattedAsync("Content Type has been imported");
        }

        private async Task Export(string[] args)
        {
            var contentType = await Get(args, false);

            if (contentType == null)
                return;

            string exportDirectory;
            if (!UmbracoHost.Current.Settings.TryGetChauffeurDirectory(out exportDirectory))
                return;

            var xml = new XDocument();

            xml.Add(packagingService.Export(contentType, false));

            var fileName = DateTime.UtcNow.ToString("yyyyMMdd") + "-" + contentType.Alias + ".xml";
            xml.Save(Path.Combine(exportDirectory, fileName));
            await Out.WriteLineFormattedAsync("Content Type has been exported with file name '{0}'", fileName);
        }

        private async Task<IContentType> Get(string[] args, bool dump = true)
        {
            if (!args.Any())
            {
                await Out.WriteLineAsync("Please provide the numerical id or alias if the doc type to get");
                return null;
            }

            IContentType contentType = null;
            var foundWith = "id";
            int id;
            if (int.TryParse(args[0], out id))
                contentType = contentTypeService.GetContentType(id);
            else
            {
                contentType = contentTypeService.GetContentType(args[0]);
                foundWith = "alias";
            }

            if (contentType == null)
            {
                await Out.WriteLineFormattedAsync("No content type found with {0} of '{1}'", foundWith, args[0]);
                return null;
            }

            // I'm sorry about the following code. In Umbraco 7.0.0 PropertyType.PropertyGroupId is:
            //  a) Not set when you do GetContentType
            //  b) An internal property (!!)
            // So to ensure that the object is proprely constructed I have to use reflection and SQL
            // and set it up myself!
{'fraction_non_alphanumeric': 0.08894414690130069, 'fraction_numerical': 0.0032517214996174446, 'mean_word_length': 1.704163434186708, 'pattern_counts': {'":': 8, '<': 8, '<?xml version=': 0, '>': 8, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 14}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4547665', 'n_tokens_mistral': 2618, 'n_tokens_neox': 2518, 'n_words': 679}