PiranhaCMS / piranha.core

Piranha CMS is the friendly editor-focused CMS for .NET that can be used both as an integrated CMS or as a headless API.
http://piranhacms.org
MIT License
2.02k stars 562 forks source link

Recommended way to do integration #1361

Closed zoinkydoink closed 4 years ago

zoinkydoink commented 4 years ago

It seems that I am going in circles, first I thought I would use the headless/rest API but then after thinking about it further it seems maybe not the right approach.

I want to achieve the following:

Steps

  1. Create a Piranha CMS with Manager using SQL Server, and ASP.NET Identity (using for satisfied step 1 above), will refer to this site as MyCore
  2. Add a site (default site at this point) called MyCoreSite

Approach 1: Integration

  1. Start a new .NET Core Razor Project - MyConsumer
  2. Add Piranha.AspNetCore nuget package to MyConsumer
  3. Add stuff to startup.cs (not really sure how much stuff is needed given this is only a consumer)
  4. Point the EF to same database as MyCore
  5. Add all copy/paste \Pages\DsiplayTemplates from MyCoreto MyConsumer
  6. Add a razor page to MyConsumer that has IApiinjected
  7. Get pages using the Api based on which page I want
  8. Render the pages

Approach 2: Headless/REST

  1. Start a new .NET Core Razor Project - MyConsumer
  2. AddPiranha.WebApi to MyCoreso enabling headless calls, call .UseApi
  3. Add Piranha.AspNetCore nuget package to MyConsumer, this is done so de-serialization of REST is made easy, otherwise I would have to create custom classes that match the result of the REST Calls
  4. Start making calls to MyCoreusing REST, render pages which requires a lot of manual work as a lot of stuff is not available that makes it easy to render pages (I assume).

It seems that integration is the way to go but I am not entirely sure.

I wish there was an example of both of these scenarios provided so it is 100% clear on what is required for both scenarios. I am sure a lot of people would find it useful.

tidyui commented 4 years ago

If you're building the client application in .NET Core and they will be deployed in such a manner that you can access the database directly I would use Option 1. To start these consumer applications you can use one of our project templates available in the NuGet package Piranha.Templates and just remove the manager packages. A couple of important notes for these kinds of setups.

  1. If you consumer applications are the ones defining the content types for the respective applications, do NOT call DeleteOrphans() when building content types. This will effectively delete the types (and cascade all of the content) that the current application is unaware of.
  2. As you're uploading media assets in a different application (MyCore) you need to use a storage implementation that can handle this scenario and doesn't rely on assets being located together with the consumer. For deployments to Azure we recommend Piranha.Azure.BlobStorage.
  3. As applications are running in several application pools you will need a distributed cache so the cache in the consumer applications doesn't go stale when you change data in the manager (MyCore). You can use any distributed cache for this, for example Redis.

Best regards

dinukasal commented 3 years ago

I am working on integrating piranha with nopcommerce. For that I'm trying the Approach 1 mentioned by @zoinkydoink, First I am trying to add piranha to a fresh Dot Net core web project, and after step 3 I get this error,

'Could not resolve a service of type 'Piranha.IApi' for the parameter 'api' of method 'Configure' on type 'MyConsumer.Startup'.'

@tidyui any idea on this error?

I can see that the third parameter(IApi api) of Configure method of Startup.cs file is injected from AddPiranha function in PiranhaExtensions.cs file. It seems the api is not injected in the new dotnet project i created and modifying.

I think what I haven't done is step 6. Any idea how to do it?

tidyui commented 3 years ago

@dinukasal If you can't resolve IApi or IDb you haven't called services.AddPiranha(..) in your ConfigureServices. Take a look at this page for documentation, or the entire Basics section if you're new to Piranha. https://piranhacms.org/docs/basics/application-setup

Best regards

dinukasal commented 3 years ago

@tidyui I have called services.AddPiranha(..). I dont know why still error comes.

tidyui commented 3 years ago

Could you post your Startup code so we can take a look at it!

Regards

dinukasal commented 3 years ago
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using Microsoft.EntityFrameworkCore;
using Microsoft.OpenApi.Models;
using Piranha;
using Piranha.AttributeBuilder;
using Piranha.Data.EF.SQLServer;
using Microsoft.Extensions.Configuration;
using Piranha.AspNetCore.Identity.SQLServer;
using MyConsumer.Models;
using Piranha.Manager;

namespace MyConsumer
{
    public class Startup
    {
        public IConfiguration Configuration { get; private set; }

        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            //services.AddRazorPages();

            services.AddPiranha(options =>
            {
                options.AddRazorRuntimeCompilation = true;

                //options.UseFileStorage(naming: FileStorageNaming.UniqueFolderNames);
                options.UseImageSharp();
                options.UseManager();
                options.UseTinyMCE();
                options.UseMemoryCache();

                //options.UseEF<SQLiteDb>(db =>
                //    db.UseSqlite("Filename=./piranha.razorweb.db"));
                //options.UseIdentityWithSeed<IdentitySQLiteDb>(//db =>
                //    db.UseSqlite("Filename=./piranha.razorweb.d//b"));
                options.UseEF<SQLServerDb>(db => db.UseSqlServer(Configuration.GetConnectionString("piranha")));
                options.UseIdentityWithSeed<IdentitySQLServerDb>(db =>
                db.UseSqlServer(Configuration.GetConnectionString("piranha")));

                options.UseSecurity(o =>
                {
                    o.UsePermission("Subscriber");
                });
            });

            services.AddSwaggerGen(options =>
            {
                options.SwaggerDoc("v1", new OpenApiInfo { Title = "PiranhaCMS API", Version = "v1" });
                options.CustomSchemaIds(x => x.FullName);
            });

        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApi api)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseSwagger();

                // Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
                // specifying the Swagger JSON endpoint.
                app.UseSwaggerUI(options =>
                {
                    options.SwaggerEndpoint("/swagger/v1/swagger.json", "PiranhaCMS API V1");
                });
            }
            else
            {
                app.UseExceptionHandler("/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            App.Init(api);

            // Configure cache level
            App.CacheLevel = Piranha.Cache.CacheLevel.Full;

            // Build content types
            new ContentTypeBuilder(api)
                .AddAssembly(typeof(Startup).Assembly)
                .Build();
                //.DeleteOrphans();

            // Configure editor
            Piranha.Manager.Editor.EditorConfig.FromFile("editorconfig.json");

            var builder = new PageTypeBuilder(api)
                .AddType(typeof(ContactPage));
            builder.Build();

            App.Modules.Manager().Scripts.Add("~/assets/js/custom-blocks.js");

            Menu.Items.Insert(2, new MenuItem
            {
                InternalId = "MyModule",
                Name = "My Menu",
                Css = "fas fa-fish"
            });

            Menu.Items["MyModule"].Items.Add(new MenuItem
            {
                InternalId = "Manager",
                Name = "Manager",
                Route = "~/manager",
                Css = "fas fa-brain",
            });
            /**
             *
             * Test another culture in the UI
             *
            var cultureInfo = new System.Globalization.CultureInfo("en-US");
            System.Globalization.CultureInfo.DefaultThreadCurrentCulture = cultureInfo;
            System.Globalization.CultureInfo.DefaultThreadCurrentUICulture = cultureInfo;
             */

            //
            // Simplified setup with dependencies
            //
            app.UsePiranha(options =>
            {
                options.UseManager();
                options.UseTinyMCE();
                options.UseIdentity();
            });

            Seed.RunAsync(api).GetAwaiter().GetResult();

            /////////////////////////////// code existed in fresh dotnet core project
            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapRazorPages();
            });
            ///////////////////////////////
        }
    }
}

Thanks @tidyui

dinukasal commented 3 years ago

It seems it was an issue with sql db connection string. But now I get this error,

System.NullReferenceException: Object reference not set to an instance of an object.
   at System.Object.GetType()
   at Piranha.Services.ContentFactory.InitFieldAsync(IServiceScope scope, Object field, Boolean managerInit)
   at Piranha.Services.ContentFactory.InitDynamicRegionAsync(IServiceScope scope, Object region, RegionType regionType, Boolean managerInit)
   at Piranha.Services.ContentFactory.InitDynamicAsync[T](T model, ContentTypeBase type, Boolean managerInit)
   at Piranha.Services.PageService.OnLoadAsync(PageBase model, Boolean isDraft)
   at Piranha.Services.PageService.GetStartpageAsync[T](Nullable`1 siteId)
   at Nop.Web.Seed.RunAsync(IApi api) in C:\Users\Dinuka\dev\mcommerce\cms integration\nop\Presentation\Nop.Web\Seed.cs:line 23
   at Nop.Web.Startup.Configure(IApplicationBuilder app, IWebHostEnvironment env, IApi api) in C:\Users\Dinuka\dev\mcommerce\cms integration\nop\Presentation\Nop.Web\Startup.cs:line 163
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.Invoke(Object instance, IApplicationBuilder builder)
   at Microsoft.AspNetCore.Hosting.ConfigureBuilder.<>c__DisplayClass4_0.<Build>b__0(IApplicationBuilder builder)
   at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.<>c__DisplayClass13_0.<UseStartup>b__2(IApplicationBuilder app)
   at Microsoft.AspNetCore.Mvc.Filters.MiddlewareFilterBuilderStartupFilter.<>c__DisplayClass0_0.<Configure>g__MiddlewareFilterBuilder|0(IApplicationBuilder builder)
   at Microsoft.AspNetCore.Server.IIS.Core.IISServerSetupFilter.<>c__DisplayClass2_0.<Configure>b__0(IApplicationBuilder app)
   at Microsoft.AspNetCore.HostFilteringStartupFilter.<>c__DisplayClass0_0.<Configure>b__0(IApplicationBuilder app)
   at Microsoft.AspNetCore.Hosting.GenericWebHostService.StartAsync(CancellationToken cancellationToken)
The thread 0x6cd4 has exited with code 0 (0x0).
Exception thrown: 'System.NullReferenceException' in System.Private.CoreLib.dll
Object reference not set to an instance of an object.

An unhandled exception of type 'System.NullReferenceException' occurred in System.Private.CoreLib.dll
Object reference not set to an instance of an object.
bbqchickenrobot commented 3 years ago

@dinukasal Did you ever get this resolved ?