Umbraco 7在自定义部分中加载内容媒体选择器

时间:2015-03-15 21:05:28

标签: c# angularjs umbraco7

我在Umbraco 7中创建了一个引用外部网址的自定义部分,但是需要扩展它以使用与“内容”富文本编辑器中的媒体选择器完全相同的功能。我不需要任何其他富文本功能,只需从图标加载媒体选择器覆盖,然后选择内部或外部URL。

我试图提取umbraco源代码,并尝试对在线教程进行各种调整,但到目前为止我无法加载媒体选择器。

我知道从根本上说我需要:

  • 另一个角度控制器,用于从内容中返回数据 'getall'方法
  • 包含媒体选择器叠加层的html部分
  • 我的自定义部分中edit.html中的引用,用于启动叠加层。

然而,到目前为止,我还没能将它们连接在一起,所以任何帮助都非常感激。

2 个答案:

答案 0 :(得分:3)

所以,这就是我提出解决方案的方法.....

第一个胜利是我发现了2个优秀的教程博客文章,在这个解决方案的基础上,非常尊重以下代码猫:

Tim Geyssons - Nibble的帖子: http://www.nibble.be/?p=440

Markus Johansson - Enkelmedia http://www.enkelmedia.se/blogg/2013/11/22/creating-custom-sections-in-umbraco-7-part-1.aspx

  1. 创建一个模型对象来表示一个关键短语,该关键短语将与一个新的简单ORM表相关联。 ToString()方法允许在前端输出友好名称。

     [TableName("Keyphrase")]
     public class Keyphrase
     {
        [PrimaryKeyColumn(AutoIncrement = true)]
        public int Id { get; set; }
        public string Name { get; set; }
        public string Phrase { get; set; }
        public string Link { get; set; }
    
        public override string ToString()
        {
            return Name;
        }
    }
    
  2. 创建一个Umbraco应用程序'这将通过实现IApplication接口注册新的自定义部分。我打电话给我的公用事业'并将其与实用程序图标相关联。

    [Application("Utilities", "Utilities", "icon-utilities", 8)]
    public class UtilitiesApplication : IApplication { }
    
  3. 装饰器允许我们提供新自定义部分的名称,别名,图标和排序顺序。

    1. 创建一个Umbraco树web控制器,它允许我们为关键短语创建所需的菜单行为,并从我们的数据库关键短语表中显示关键短语集合。

      [PluginController("Utilities")]
      [Umbraco.Web.Trees.Tree("Utilities", "KeyphraseTree", "Keyphrase", iconClosed: "icon-doc", sortOrder: 1)]
      public class KeyphraseTreeController : TreeController
      {
          private KeyphraseApiController _keyphraseApiController;
      
          public KeyphraseTreeController()
          {
               _keyphraseApiController = new KeyphraseApiController();
          }
      
          protected override TreeNodeCollection GetTreeNodes(string id,                                                                FormDataCollection queryStrings)
          {
              var nodes = new TreeNodeCollection();
              var keyphrases = _keyphraseApiController.GetAll();
      
              if (id == Constants.System.Root.ToInvariantString())
              {
                  foreach (var keyphrase in keyphrases)
                  {
                      var node = CreateTreeNode(
                      keyphrase.Id.ToString(),
                      "-1",
                      queryStrings,
                      keyphrase.ToString(),
                      "icon-book-alt",
                      false);
      
                      nodes.Add(node);
                  }
              }
      
              return nodes;
          }
      
          protected override MenuItemCollection GetMenuForNode(string id,                                                               FormDataCollection queryStrings)
          {
              var menu = new MenuItemCollection();
      
              if (id == Constants.System.Root.ToInvariantString())
              {
                  // root actions
                   menu.Items.Add<CreateChildEntity, ActionNew>(ui.Text("actions", ActionNew.Instance.Alias));
                  menu.Items.Add<RefreshNode, ActionRefresh>(ui.Text("actions", ActionRefresh.Instance.Alias), true);
                  return menu;
              }
              else
              {
                  menu.Items.Add<ActionDelete>(ui.Text("actions", ActionDelete.Instance.Alias));
              }
              return menu;
          }
      }
      
    2. 类装饰器和TreeController扩展允许我们为关键短语树声明Web控制器,将其与我们的Utilities自定义部分相关联,以及选择图标和排序顺序。

      我们还声明了一个api控制器(我们将会这样做!),这将允许我们访问我们的Keyphrase数据对象。

      GetTreeNodes方法允许我们迭代关键短语数据集合并将结果节点返回到视图。

      GetMenuNode方法允许我们为自定义部分创建所需的菜单选项。 我们声明如果节点是根(Utilities),那么允许我们添加子节点并刷新节点集合。 但是,如果我们在节点树(Keyphrase)中较低,那么我们只希望用户能够删除该节点(即,不允许用户创建比Keyphrase更深的另一级节点)

      1. 为我们的Keyphrase CRUD请求创建一个api控制器

        public class KeyphraseApiController : UmbracoAuthorizedJsonController
        {
            public IEnumerable<Keyphrase> GetAll()
            {
                var query = new Sql().Select("*").From("keyphrase");
                return DatabaseContext.Database.Fetch<Keyphrase>(query);
            }
        
            public Keyphrase GetById(int id)
            {
                var query = new Sql().Select("*").From("keyphrase").Where<Keyphrase>(x => x.Id == id);
                return DatabaseContext.Database.Fetch<Keyphrase>(query).FirstOrDefault();
            }
        
            public Keyphrase PostSave(Keyphrase keyphrase)
            {
                if (keyphrase.Id > 0)
                    DatabaseContext.Database.Update(keyphrase);
                else
                    DatabaseContext.Database.Save(keyphrase);
        
                return keyphrase;
            }
        
            public int DeleteById(int id)
            {
                return DatabaseContext.Database.Delete<Keyphrase>(id);
            }
        }
        
      2. 使用角度控制器创建自定义剖面视图,这是Umbraco 7中当前的架构风格。 应该注意的是,Umbraco希望您的自定义部分组件放入以下结构App_Plugins // BackOffice /

      3. 我们需要一个视图来显示和编辑我们的关键短语名称,目标短语和网址

            <form name="keyphraseForm"
                ng-controller="Keyphrase.KeyphraseEditController"
                ng-show="loaded"
                ng-submit="save(keyphrase)"
                val-form-manager>
                    <umb-panel>
                        <umb-header>
                            <div class="span7">
                                <umb-content-name placeholder=""
                                                  ng-model="keyphrase.Name" />
                            </div>
        
                            <div class="span5">
                                <div class="btn-toolbar pull-right umb-btn-toolbar">
                                    <umb-options-menu ng-show="currentNode"
                                                      current-node="currentNode"
                                                      current-section="{{currentSection}}">
                                    </umb-options-menu>
                                </div>
                            </div>
                        </umb-header>
        
                        <div class="umb-panel-body umb-scrollable row-fluid">
                            <div class="tab-content form-horizontal" style="padding-bottom: 90px">
                                <div class="umb-pane">
                                    <umb-control-group label="Target keyphrase"                                                   description="Keyphrase to be linked'">
                                        <input type="text" class="umb-editor umb-textstring" ng-model="keyphrase.Phrase" required />
                                    </umb-control-group>
        
                                    <umb-control-group label="Keyphrase link" description="Internal or external url">
                                        <p>{{keyphrase.Link}}</p>
                                        <umb-link-picker ng-model="keyphrase.Link" required/>
                                    </umb-control-group>
        
                                    <div class="umb-tab-buttons" detect-fold>
                                        <div class="btn-group">
                                            <button type="submit" data-hotkey="ctrl+s" class="btn btn-success">
                                                <localize key="buttons_save">Save</localize>
                                            </button>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </umb-panel>
                </form>
        

        这利用umbraco和角度标记动态显示数据输入字段,并将我们的视图关联到与数据层交互的角度控制器

            angular.module("umbraco").controller("Keyphrase.KeyphraseEditController",
            function ($scope, $routeParams, keyphraseResource, notificationsService, navigationService) {
        
                $scope.loaded = false;
        
                if ($routeParams.id == -1) {
                    $scope.keyphrase = {};
                    $scope.loaded = true;
                }
                else {
                    //get a keyphrase id -> service
                    keyphraseResource.getById($routeParams.id).then(function (response) {
                    $scope.keyphrase = response.data;
                    $scope.loaded = true;
                });
            }
        
            $scope.save = function (keyphrase) {
                keyphraseResource.save(keyphrase).then(function (response) {
                    $scope.keyphrase = response.data;
                    $scope.keyphraseForm.$dirty = false;
                    navigationService.syncTree({ tree: 'KeyphraseTree', path: [-1, -1], forceReload: true });
                    notificationsService.success("Success", keyphrase.Name + " has been saved");
                });
            };
        
        });
        

        然后我们需要html和相应的角度控制器来进行关键短语删除行为

            <div class="umb-pane" ng-controller="Keyphrase.KeyphraseDeleteController">
                <p>
                    Are you sure you want to delete {{currentNode.name}} ?
                </p>
                <div>
                    <div class="umb-pane btn-toolbar umb-btn-toolbar">
                        <div class="control-group umb-control-group">
                            <a href="" class="btn btn-link" ng-click="cancelDelete()"
                                <localize key="general_cancel">Cancel</localize>
                            </a>
                            <a href="" class="btn btn-primary" ng-click="delete(currentNode.id)">
                                <localize key="general_ok">OK</localize>
                            </a>
                        </div>
                    </div>
                </div>
            </div>
        
        1. 利用Umbraco的linkpicker允许用户选择内部或外部网址。 我们需要html标记来启动LinkPicker

          <div>
              <ul class="unstyled list-icons">
                  <li>
                      <i class="icon icon-add blue"></i>
                      <a href ng-click="openLinkPicker()" prevent-default>Select</a>
                  </li>
              </ul>
          </div>
          
        2. 一个关联的指令js文件,用于启动链接选择器并将选定的URL发布回html视图

              angular.module("umbraco.directives")
                  .directive('umbLinkPicker', function (dialogService, entityResource)         {
                      return {
                          restrict: 'E',
                          replace: true,
                          templateUrl: '/App_Plugins/Utilities/umb-link-picker.html',
                          require: "ngModel",
                          link: function (scope, element, attr, ctrl) {
          
                              ctrl.$render = function () {
                                  var val = parseInt(ctrl.$viewValue);
          
                                  if (!isNaN(val) && angular.isNumber(val) && val > 0) {
          
                                      entityResource.getById(val, "Content").then(function (item) {
                                          scope.node = item;
                                      });
                                  }
                          };
          
                          scope.openLinkPicker = function () {
                              dialogService.linkPicker({ callback: populateLink });
                          }
          
                          scope.removeLink = function () {
                              scope.node = undefined;
                              updateModel(0);
                          }
          
                          function populateLink(item) {
                              scope.node = item;
                              updateModel(item.url);
          
                          }
          
                          function updateModel(id) {
                              ctrl.$setViewValue(id);
          
                          }
                      }
                  };
              });
          

          有一个最终的js文件允许我们通过网络发送数据,每个人最喜欢的http动词GET,POST(句柄也放在这里)和DELETE

              angular.module("umbraco.resources")
                  .factory("keyphraseResource", function ($http) {
                      return {
                          getById: function (id) {
                              return $http.get("BackOffice/Api/KeyphraseApi/GetById?id=" + id);
                          },
                          save: function (keyphrase) {
                              return $http.post("BackOffice/Api/KeyphraseApi/PostSave", angular.toJson(keyphrase));
                          },
                          deleteById: function (id) {
                              return $http.delete("BackOffice/Api/KeyphraseApi/DeleteById?id=" + id);
                          }
                      };
                  });
          

          此外,我们需要一个包清单来注册我们的javascript行为

              {
                  javascript: [
                  '~/App_Plugins/Utilities/BackOffice/KeyphraseTree/edit.controller.js',
                      '~/App_Plugins/Utilities/BackOffice/KeyphraseTree/delete.controller.js',
                  '~/App_Plugins/Utilities/keyphrase.resource.js',
                  '~/App_Plugins/Utilities/umbLinkPicker.directive.js'
                  ]
              }
          
          1. 实施调整以允许解决方案的CMS部分正常工作。 在这一点上,我们几乎得到了我们的定制部分唱歌,但我们只需要跳几个Umbraco箍,即 a)添加一个keyphrase事件类,如果它不存在,则创建我们的关键短语db表(参见第8点) b)启动Umbraco并将新的自定义部分与目标用户关联(从“用户”菜单) c)通过在umbraco中搜索它来更改自定义部分的占位符文本 - > gt> config - &gt; en.xml并交换&#39;实用程序&#39;

          2. 在保存或发布内容时拦截目标数据类型的目标内容字段 我给出的要求是拦截新闻文章的正文内容,因此您需要在Umbraco中创建一个文档类型,例如,标题字段类型为&#39; Textstring&#39;,和&#39; Richtext编辑器&#39;。

          3. 类型的bodyContent字段

            您还需要一个或多个关键短语定位,现在应该在新的Umbraco自定义栏目中,&#39; Utilities&#39;

            在这里,我定位了关键短语&#39;技术新闻&#39;链接到英国广播公司技术新闻网站,以便我随时写下短语“科技新闻”。 href链接将自动插入。 这显然是一个非常简单的例子,但如果用户需要链接到某些重复的法律文件,例如税收,财产,尽职调查等,可以在外部或在CMS本身内托管,这将非常有用。 href链接将在新选项卡中打开外部资源,并在同一窗口中打开内部资源(我们将在第9点中找到它)

            因此,我们尝试实现的原则是拦截文档的Umbraco保存事件并操纵我们的富文本以插入我们的链接。这样做如下:  a)建立一种方法(ContentServiceOnSaving),当用户点击“保存”或“发布并保存”时,该方法将触发。  b)定位我们想要的内容字段以找到我们的关键短语。  c)根据我们的密钥短语集合解析目标内容html,以创建我们的内部/外部链接。

            注意:如果您只想启动并运行自定义部分,则只需要ApplicationStarted方法即可创建KeyPhrase表。

                public class KeyphraseEvents : ApplicationEventHandler
                {
                    private KeyphraseApiController _keyphraseApiController;
            
                    protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, 
                                                       ApplicationContext applicationContext)
                    {
                        _keyphraseApiController = new KeyphraseApiController();
                        ContentService.Saving += ContentServiceOnSaving;
                        var db = applicationContext.DatabaseContext.Database;
            
                        if (!db.TableExist("keyphrase"))
                        {
                            db.CreateTable<Keyphrase>(false);
                        }
                    }
            
                    private void ContentServiceOnSaving(IContentService sender, SaveEventArgs<IContent> saveEventArgs)
                    {
                        var keyphrases = _keyphraseApiController.GetAll();
                        var keyphraseContentParser = new KeyphraseContentParser();
            
                        foreach (IContent content in saveEventArgs.SavedEntities)
                        {
                            if (content.ContentType.Alias.Equals("NewsArticle"))
                            {
                                var blogContent = content.GetValue<string>("bodyContent");
                                var parsedBodyText = keyphraseContentParser.ReplaceKeyphrasesWithLinks(blogContent, keyphrases);
                                content.SetValue("bodyContent", parsedBodyText);
                            }
                        }
                    }
                }
            

            ContentServiceOnSaving方法允许我们拦截Umbraco中的任何保存事件。然后我们检查我们的传入内容,看看它是否符合我们预期的类型 - 在本例中&#39; NewsArticle&#39; - 如果是,则定位&#39; bodyContent&#39;部分,使用我们的&#39; KeyphraseContentParser&#39;解析它,并交换当前的&#39; bodyContent&#39;使用已解析的&bodyContent&#39;。

            1. 创建一个Keyphrase解析器来交换内部/外部链接的关键短语

              public class KeyphraseContentParser
              {
                  public string ReplaceKeyphrasesWithLinks(string htmlContent,                                                        IEnumerable<Keyphrase> keyphrases)
                  {
                      var parsedHtmlStringBuilder = new StringBuilder(htmlContent);
              
                      foreach (var keyphrase in keyphrases)
                      {
                          if (htmlContent.CaseContains(keyphrase.Phrase, StringComparison.OrdinalIgnoreCase))
                          {
                              var index = 0;
                              do
                              {
                                  index = parsedHtmlStringBuilder.ToString()
                                  .IndexOf(keyphrase.Phrase, index, StringComparison.OrdinalIgnoreCase);
              
                                  if (index != -1)
                                  {
                                      var keyphraseSuffix = parsedHtmlStringBuilder.ToString(index, keyphrase.Phrase.Length + 4);
                                      var keyPhraseFromContent = parsedHtmlStringBuilder.ToString(index, keyphrase.Phrase.Length);
              
                                      var keyphraseTarget = "_blank";
              
                                      if (keyphrase.Link.StartsWith("/"))
                                      {
                                          keyphraseTarget = "_self";
                                      }
              
                                      var keyphraseLinkReplacement = String.Format("<a href='{0}' target='{1}'>{2}</a>",
              keyphrase.Link, keyphraseTarget, keyPhraseFromContent);
              
                                      if (!keyphraseSuffix.Equals(String.Format("{0}</a>", keyPhraseFromContent)))
                                      {
                                          parsedHtmlStringBuilder.Remove(index, keyPhraseFromContent.Length);
                                          parsedHtmlStringBuilder.Insert(index, keyphraseLinkReplacement);
                                          index += keyphraseLinkReplacement.Length;
                                      }
                                      else
                                      {
                                          var previousStartBracket = parsedHtmlStringBuilder.ToString().LastIndexOf("<a", index);
                                          var nextEndBracket = parsedHtmlStringBuilder.ToString().IndexOf("a>", index);
              
                                        parsedHtmlStringBuilder.Remove(previousStartBracket, (nextEndBracket - (previousStartBracket - 2)));
                                             parsedHtmlStringBuilder.Insert(previousStartBracket, keyphraseLinkReplacement);
                                  index = previousStartBracket +         keyphraseLinkReplacement.Length;
                                      }
                                  }
                              } while (index != -1);
                          }
                      }
              
                      return parsedHtmlStringBuilder.ToString();
                  }
              }
              
            2. 通过上面的代码可能最容易,但从根本上说,解析器必须:

              a)找到并包装所有关键短语,忽略大小写,包含指向内部CMS或外部网络资源的链接。

              b)处理已解析的html字符串,以保留链接到位,而不是创建嵌套链接。

              c)允许在解析的html字符串中更新CMS关键短语更改。

              此博客以及github代码可以从上一篇文章的链接中找到。

答案 1 :(得分:0)

好的,所以在找到一些优秀的帮助帖并挖掘后,我想出了解决方案,我在这里写过: http://frazzledcircuits.blogspot.co.uk/2015/03/umbraco-7-automatic-keyphrase.html

源代码在这里: https://github.com/AdTarling/UmbracoSandbox