2017-02-14 15 views
1

Я ищу способ показать псевдоним типа документа или имя выбранного контента в многоуровневой сборщике деревьев внутри содержимого., показывающий псевдоним типа документа или имя выбранного узла в Multi node tree picker внутри вкладки содержимого в Umbraco V7

Это очень поможет мне быстро определить тип прилагаемого контента. Также, зависание контента, добавленного в MNTP внутри контента, показывает числовой путь, который не очень полезен, есть ли способ показать имена путей вместо идентификаторов?

Прикрепить изображение для справки.

Просьба предложить. enter image description here

ответ

0

Самый простой способ сделать это - просто изменить редактор свойств MNTP и показать ему дополнительные данные. Однако не рекомендуется изменять эти файлы, так как он будет возвращен при каждом обновлении вашего сайта Umbraco.

Существует небольшое хакерское решение для достижения того, чего вы хотите;


Угловое имеет что-то называемое перехватчиками, позволяющее вам перехватывать и изменять сделанные запросы. Регистрируя перехватчик, вы можете перехватить запросы на contentpicker.html и перенаправить его в файл contentpicker.html, расположенный где-то в другом месте - это означает, что вы сможете просто скопировать этот файл в другое место и не изменять тот, который находится в папке umbraco, перезаписываемой обновлениями.

К сожалению, в этом случае недостаточно просто переопределить представление, отправляемое в браузер, поскольку псевдоним типа документа на самом деле не доступен для этого представления - так что представление никогда не сможет показать псевдоним, даже если мы его модифицировали.

Чтобы обойти это, вы можете создать копию файла contentpicker.controller.js и создать модифицированную версию этого файла, которая будет использоваться в пользовательском виде contentpicker.html. Этот модифицированный ContentPickerController обязательно включит contentTypeAlias в модель, используемую для визуализации представления.

Все это может быть обернуто как «пакет» в папке App_Plugins, и оно будет прекрасно отделено от перезаписи при обновлении Umbraco при автоматической загрузке через файл package.manifest. Единственное предостережение в том, что в случае, если что-то обновляется в коде для выбора контента, вам придется вручную объединить эти обновления в свои файлы для выбора контента - к счастью, сборщик контента редко обновляется.

Поместите следующие файлы в App_Plugins/CustomContentPicker/ папке, и вы должны иметь то, что вы хотите без фактического изменения основного кода:

package.manifest

{ 
    "javascript": [ 
    "~/App_Plugins/CustomContentPicker/customcontentpicker.controller.js" 
    ] 
} 

customcontentpicker.html

<div ng-controller="Umbraco.PropertyEditors.CustomContentPickerController" class="umb-editor umb-contentpicker"> 

    <ng-form name="contentPickerForm"> 

     <ul class="unstyled list-icons" 
      ui-sortable 
      ng-model="renderModel"> 
      <li ng-repeat="node in renderModel" ng-attr-title="{{model.config.showPathOnHover && 'Path: ' + node.path || undefined}}"> 
       <i class="icon icon-navigation handle"></i> 
       <a href="#" prevent-default ng-click="remove($index)"> 
        <i class="icon icon-delete red hover-show"></i> 
        <i class="{{node.icon}} hover-hide"></i> 
        {{node.name}}<br /> 
        ({{node.contentTypeAlias}}) 
       </a> 

       <div ng-if="!dialogEditor && ((model.config.showOpenButton && allowOpenButton) || (model.config.showEditButton && allowEditButton))"> 
        <small ng-if="model.config.showOpenButton && allowOpenButton"><a href ng-click="showNode($index)"><localize key="open">Open</localize></a></small> 
        <small ng-if="model.config.showEditButton && allowEditButton"><a href umb-launch-mini-editor="node"><localize key="edit">Edit</localize></a></small> 
       </div> 
      </li> 
     </ul> 

     <ul class="unstyled list-icons" ng-show="model.config.multiPicker === true || renderModel.length === 0"> 
      <li> 
       <i class="icon icon-add blue"></i> 
       <a href="#" ng-click="openContentPicker()" prevent-default> 
        <localize key="general_add">Add</localize> 
       </a> 
      </li> 
     </ul> 

     <!--These are here because we need ng-form fields to validate against--> 
     <input type="hidden" name="minCount" ng-model="renderModel" /> 
     <input type="hidden" name="maxCount" ng-model="renderModel" /> 

     <div class="help-inline" val-msg-for="minCount" val-toggle-msg="minCount"> 
      You need to add at least {{model.config.minNumber}} items 
     </div> 

     <div class="help-inline" val-msg-for="maxCount" val-toggle-msg="maxCount"> 
      You can only have {{model.config.maxNumber}} items selected 
     </div> 


    </ng-form> 

    <umb-overlay 
     ng-if="contentPickerOverlay.show" 
     model="contentPickerOverlay" 
     view="contentPickerOverlay.view" 
     position="right"> 
    </umb-overlay> 

</div> 

customcontentpicker.controller.js

angular.module('umbraco.services').config([ 
    '$httpProvider', 
    function ($httpProvider) { 
     $httpProvider.interceptors.push(function ($q) { 
      return { 
       'request': function (request) { 
        var url = 'views/propertyeditors/contentpicker/contentpicker.html'; 
        if (request.url.indexOf(url) !== -1) { 
         request.url = request.url.replace(url, '/App_Plugins/CustomContentPicker/customcontentpicker.html'); 
        } 
        return request || $q.when(request); 
       } 
      }; 
     }); 
    }]); 

// Below is contentpicker.controller.js modified 

//this controller simply tells the dialogs service to open a mediaPicker window 
//with a specified callback, this callback will receive an object with a selection on it 

function customContentPickerController($scope, dialogService, entityResource, editorState, $log, iconHelper, $routeParams, fileManager, contentEditingHelper, angularHelper, navigationService, $location) { 

    function trim(str, chr) { 
     var rgxtrim = (!chr) ? new RegExp('^\\s+|\\s+$', 'g') : new RegExp('^' + chr + '+|' + chr + '+$', 'g'); 
     return str.replace(rgxtrim, ''); 
    } 

    function startWatch() { 
     //We need to watch our renderModel so that we can update the underlying $scope.model.value properly, this is required 
     // because the ui-sortable doesn't dispatch an event after the digest of the sort operation. Any of the events for UI sortable 
     // occur after the DOM has updated but BEFORE the digest has occured so the model has NOT changed yet - it even states so in the docs. 
     // In their source code there is no event so we need to just subscribe to our model changes here. 
     //This also makes it easier to manage models, we update one and the rest will just work. 
     $scope.$watch(function() { 
      //return the joined Ids as a string to watch 
      return _.map($scope.renderModel, function (i) { 
       return i.id; 
      }).join(); 
     }, function (newVal) { 
      var currIds = _.map($scope.renderModel, function (i) { 
       return i.id; 
      }); 
      $scope.model.value = trim(currIds.join(), ","); 

      //Validate! 
      if ($scope.model.config && $scope.model.config.minNumber && parseInt($scope.model.config.minNumber) > $scope.renderModel.length) { 
       $scope.contentPickerForm.minCount.$setValidity("minCount", false); 
      } 
      else { 
       $scope.contentPickerForm.minCount.$setValidity("minCount", true); 
      } 

      if ($scope.model.config && $scope.model.config.maxNumber && parseInt($scope.model.config.maxNumber) < $scope.renderModel.length) { 
       $scope.contentPickerForm.maxCount.$setValidity("maxCount", false); 
      } 
      else { 
       $scope.contentPickerForm.maxCount.$setValidity("maxCount", true); 
      } 
     }); 
    } 

    $scope.renderModel = []; 

    $scope.dialogEditor = editorState && editorState.current && editorState.current.isDialogEditor === true; 

    //the default pre-values 
    var defaultConfig = { 
     multiPicker: false, 
     showOpenButton: false, 
     showEditButton: false, 
     showPathOnHover: false, 
     startNode: { 
      query: "", 
      type: "content", 
      id: $scope.model.config.startNodeId ? $scope.model.config.startNodeId : -1 // get start node for simple Content Picker 
     } 
    }; 

    if ($scope.model.config) { 
     //merge the server config on top of the default config, then set the server config to use the result 
     $scope.model.config = angular.extend(defaultConfig, $scope.model.config); 
    } 

    //Umbraco persists boolean for prevalues as "0" or "1" so we need to convert that! 
    $scope.model.config.multiPicker = ($scope.model.config.multiPicker === "1" ? true : false); 
    $scope.model.config.showOpenButton = ($scope.model.config.showOpenButton === "1" ? true : false); 
    $scope.model.config.showEditButton = ($scope.model.config.showEditButton === "1" ? true : false); 
    $scope.model.config.showPathOnHover = ($scope.model.config.showPathOnHover === "1" ? true : false); 

    var entityType = $scope.model.config.startNode.type === "member" 
     ? "Member" 
     : $scope.model.config.startNode.type === "media" 
     ? "Media" 
     : "Document"; 
    $scope.allowOpenButton = entityType === "Document" || entityType === "Media"; 
    $scope.allowEditButton = entityType === "Document"; 

    //the dialog options for the picker 
    var dialogOptions = { 
     multiPicker: $scope.model.config.multiPicker, 
     entityType: entityType, 
     filterCssClass: "not-allowed not-published", 
     startNodeId: null, 
     callback: function (data) { 
      if (angular.isArray(data)) { 
       _.each(data, function (item, i) { 
        $scope.add(item); 
       }); 
      } else { 
       $scope.clear(); 
       $scope.add(data); 
      } 
      angularHelper.getCurrentForm($scope).$setDirty(); 
     }, 
     treeAlias: $scope.model.config.startNode.type, 
     section: $scope.model.config.startNode.type 
    }; 

    //since most of the pre-value config's are used in the dialog options (i.e. maxNumber, minNumber, etc...) we'll merge the 
    // pre-value config on to the dialog options 
    angular.extend(dialogOptions, $scope.model.config); 

    //We need to manually handle the filter for members here since the tree displayed is different and only contains 
    // searchable list views 
    if (entityType === "Member") { 
     //first change the not allowed filter css class 
     dialogOptions.filterCssClass = "not-allowed"; 
     var currFilter = dialogOptions.filter; 
     //now change the filter to be a method 
     dialogOptions.filter = function (i) { 
      //filter out the list view nodes 
      if (i.metaData.isContainer) { 
       return true; 
      } 
      if (!currFilter) { 
       return false; 
      } 
      //now we need to filter based on what is stored in the pre-vals, this logic duplicates what is in the treepicker.controller, 
      // but not much we can do about that since members require special filtering. 
      var filterItem = currFilter.toLowerCase().split(','); 
      var found = filterItem.indexOf(i.metaData.contentType.toLowerCase()) >= 0; 
      if (!currFilter.startsWith("!") && !found || currFilter.startsWith("!") && found) { 
       return true; 
      } 

      return false; 
     } 
    } 


    //if we have a query for the startnode, we will use that. 
    if ($scope.model.config.startNode.query) { 
     var rootId = $routeParams.id; 
     entityResource.getByQuery($scope.model.config.startNode.query, rootId, "Document").then(function (ent) { 
      dialogOptions.startNodeId = ent.id; 
     }); 
    } else { 
     dialogOptions.startNodeId = $scope.model.config.startNode.id; 
    } 

    //dialog 
    $scope.openContentPicker = function() { 
     $scope.contentPickerOverlay = dialogOptions; 
     $scope.contentPickerOverlay.view = "treepicker"; 
     $scope.contentPickerOverlay.show = true; 

     $scope.contentPickerOverlay.submit = function (model) { 

      if (angular.isArray(model.selection)) { 
       _.each(model.selection, function (item, i) { 
        $scope.add(item); 
       }); 
      } 

      $scope.contentPickerOverlay.show = false; 
      $scope.contentPickerOverlay = null; 
     } 

     $scope.contentPickerOverlay.close = function (oldModel) { 
      $scope.contentPickerOverlay.show = false; 
      $scope.contentPickerOverlay = null; 
     } 

    }; 

    $scope.remove = function (index) { 
     $scope.renderModel.splice(index, 1); 
     angularHelper.getCurrentForm($scope).$setDirty(); 
    }; 

    $scope.showNode = function (index) { 
     var item = $scope.renderModel[index]; 
     var id = item.id; 
     var section = $scope.model.config.startNode.type.toLowerCase(); 

     entityResource.getPath(id, entityType).then(function (path) { 
      navigationService.changeSection(section); 
      navigationService.showTree(section, { 
       tree: section, path: path, forceReload: false, activate: true 
      }); 
      var routePath = section + "/" + section + "/edit/" + id.toString(); 
      $location.path(routePath).search(""); 
     }); 
    } 

    $scope.add = function (item) { 
     var currIds = _.map($scope.renderModel, function (i) { 
      return i.id; 
     }); 

     if (currIds.indexOf(item.id) < 0) { 
      item.icon = iconHelper.convertFromLegacyIcon(item.icon); 
      $scope.renderModel.push({ name: item.name, id: item.id, icon: item.icon, path: item.path, contentTypeAlias: item.metaData.ContentTypeAlias }); 
     } 
    }; 

    $scope.clear = function() { 
     $scope.renderModel = []; 
    }; 

    var unsubscribe = $scope.$on("formSubmitting", function (ev, args) { 
     var currIds = _.map($scope.renderModel, function (i) { 
      return i.id; 
     }); 
     $scope.model.value = trim(currIds.join(), ","); 
    }); 

    //when the scope is destroyed we need to unsubscribe 
    $scope.$on('$destroy', function() { 
     unsubscribe(); 
    }); 

    //load current data 
    var modelIds = $scope.model.value ? $scope.model.value.split(',') : []; 
    entityResource.getByIds(modelIds, entityType).then(function (data) { 

     //Ensure we populate the render model in the same order that the ids were stored! 
     _.each(modelIds, function (id, i) { 
      var entity = _.find(data, function (d) { 
       return d.id == id; 
      }); 

      if (entity) { 
       entity.icon = iconHelper.convertFromLegacyIcon(entity.icon); 
       $scope.renderModel.push({ name: entity.name, id: entity.id, icon: entity.icon, path: entity.path, contentTypeAlias: entity.metaData.ContentTypeAlias }); 
      } 


     }); 

     //everything is loaded, start the watch on the model 
     startWatch(); 

    }); 
} 

angular.module('umbraco').controller("Umbraco.PropertyEditors.CustomContentPickerController", customContentPickerController);