• Icon: Task Task
    • Resolution: Fixed
    • Icon: Major Major
    • 3.7
    • 3.7
    • Plugin : Web
    • None

      Implements hierarchical tags.

          [CMS-5485] Hierarchical tags

          New patchs (and repository scripts have been updated)

          Thibaut Rizzi (Inactive) added a comment - New patchs (and repository scripts have been updated)

          New patchs (and repository scripts have been updated)

          Thibaut Rizzi (Inactive) added a comment - New patchs (and repository scripts have been updated)

          Patch v1 (work in progress)

          Thibaut Rizzi (Inactive) added a comment - Patch v1 (work in progress)

          Thibaut Rizzi (Inactive) added a comment - - edited

          Migration script part 2 :

          importClass(org.apache.commons.lang.StringUtils);
          importClass(org.apache.jackrabbit.util.Text);
          importClass(javax.jcr.NodeIterator);
          importClass(org.apache.jackrabbit.commons.predicate.NodeTypePredicate);
          importClass(org.apache.jackrabbit.spi.commons.iterator.Iterators);
          importClass(org.apache.jackrabbit.spi.commons.iterator.Predicate);
          
          var TMP_CAT_SUFFIX = '__TO_BE_MIGRATED';
          var IS_TAG_PREDICATE = new NodeTypePredicate("ametys:tag", true);
          // Not working because ametys:tag-category is not defined as a nodetype anymore.
          // var IS_CATEGORY_PREDICATE = new NodeTypePredicate("ametys:tag-category", true);
          var IS_CATEGORY_PREDICATE = new Predicate() {
            evaluate: function(node) {
                return node.getProperty('jcr:primaryType').getString() == 'ametys:tag-category';
              }
          }
          
          var ID_MAPPER = {};
          var catCount = 0;
          
          var deductTagTarget = function(catNode)
          {
            // Filter on category children.
            var tagNodes = Iterators.filterIterator(Iterators.nodes(catNode.getNodes()), new Predicate() {
              evaluate: function(node) {
                return IS_TAG_PREDICATE.evaluate(node);
              }
            });
          
            if (tagNodes.hasNext())
            {
              var tagNode = tagNodes.next();
              return tagNode.hasProperty('ametys-internal:target') ? tagNode.getProperty('ametys-internal:target').getString() : null;
            }
          
            return null;
          }
          
          
          var migrateCategory = function(catNode, s)
          {
            var parent = catNode.getParent();
            var tagInfo = {
              name: StringUtils.removeEnd(catNode.getName(), TMP_CAT_SUFFIX),
              title: catNode.hasProperty('ametys-internal:title') ? catNode.getProperty('ametys-internal:title').getString() : 'Unknown title',
              description: catNode.hasProperty('ametys-internal:description') ? catNode.getProperty('ametys-internal:description').getString() : 'Unknown description',
              visibility: 'PUBLIC',
              target: deductTagTarget(catNode) || 'CONTENT'
            };
           
            // Add and populate a new tag node
            var tagNode = parent.addNode(tagInfo.name, 'ametys:tag');
          
            tagNode.setProperty('ametys-internal:title', tagInfo.title);
            if (tagInfo.description) tagNode.setProperty('ametys-internal:description', tagInfo.description);
            tagNode.setProperty('ametys-internal:visibility', tagInfo.visibility);
            tagNode.setProperty('ametys-internal:target', tagInfo.target);
          
            // Keep track of old identifer <-> new tag name to be able update service parameters below
            ID_MAPPER[catNode.getIdentifier()] = Text.unescapeIllegalJcrChars(tagNode.getName());
          
            // Add category child nodes to the new tag.
            var childNodes = catNode.getNodes();
            while (childNodes.hasNext())
            {
              var childNode = childNodes.next();
              s.move(childNode.getPath(), tagNode.getPath() + '/' + childNode.getName());
            }
          
            // Remove tag category node.
            catNode.remove();
          }
          
          var migrateCategories = function(parentNode, s)
          {
            // Filter on category children.
            var catNodes = Iterators.filterIterator(Iterators.nodes(parentNode.getNodes()), IS_CATEGORY_PREDICATE);
          
            while (catNodes.hasNext()) {
              var catNode = catNodes.next();
          
              // Recursive preprocessing
              migrateCategories(catNode);
          
              if (StringUtils.endsWith(catNode.getName(), TMP_CAT_SUFFIX))
              {
                migrateCategory(catNode, s);
                catCount++;
              }
            }
          }
          
          // 1 - Migrate categories...
          var migrateStep1 = function(s)
          {
            var qm = s.getWorkspace().getQueryManager();
            var query = qm.createQuery("//element(*, ametys:site)/ametys-internal:plugins/web/tags", javax.jcr.query.Query.XPATH);
            var rootTagsNodes = query.execute().getNodes();
          
            while (rootTagsNodes.hasNext())
            {
              migrateCategories(rootTagsNodes.next(), s);
            }
          }
          
          migrateStep1(session);
          migrateStep1(liveSession);
          
          // 2 - Migrate impacted service parameters
          var serviceParamNames = {
            'org.ametys.plugins.calendar.Agenda': 'ametys:tag-categories',
            'org.ametys.plugins.tagcloud.services.Tags': 'ametys:search-by-tags',
            'org.ametys.web.service.FrontSearchService': 'ametys:search-by-tags'
          };
          
          var serviceCount = 0;
          var serviceChange = false;
          
          var migrateStep2 = function(s)
          {
            var qm = s.getWorkspace().getQueryManager();
            var serviceQueryPart = "@ametys-internal:service='org.ametys.plugins.calendar.Agenda' or @ametys-internal:service='org.ametys.plugins.tagcloud.services.Tags' or @ametys-internal:service='org.ametys.web.service.FrontSearchService'";
            var query = qm.createQuery("//element(*, ametys:zoneItem)[@ametys-internal:type='SERVICE' and (" + serviceQueryPart + ")]", javax.jcr.query.Query.XPATH);
            var serviceNodes = query.execute().getNodes();
          
            while (serviceNodes.hasNext())
            {
              var serviceNode = serviceNodes.next();
              var serviceId = serviceNode.getProperty('ametys-internal:service').getString();
              var paramsNode = serviceNode.getNode('ametys:service_parameters');
              var paramName = serviceParamNames[serviceId];
              serviceChange = false;
          
              if (paramsNode.hasProperty(paramName))
              {
                var values = StringUtils.split(paramsNode.getProperty(paramName).getString(), ',');
                if (values != null)
                {
                  for (var idx in values)
                  {
                    var v = values[idx];
                    if (StringUtils.startsWith(v, "tag-category://"))
                    {
                      var id = StringUtils.removeStart(v, "tag-category://");
                      values[idx] = ID_MAPPER[id] || values[idx];
                      serviceChange = true;
                    }
                  }
          
                  // update service property
                  paramsNode.setProperty(paramName, StringUtils.join(values, ','));
                }
              }
          
              if (serviceChange)
              {
                serviceCount++;
              }
            }
          }
          
          migrateStep2(session);
          migrateStep2(liveSession);
          
          session.save();
          liveSession.save();
          
          println("Successfully migrated " + catCount + " tag categories.");
          println("Successfully migrated " + serviceCount + " impacted service.");
          

          Thibaut Rizzi (Inactive) added a comment - - edited Migration script part 2 : importClass(org.apache.commons.lang.StringUtils); importClass(org.apache.jackrabbit.util.Text); importClass(javax.jcr.NodeIterator); importClass(org.apache.jackrabbit.commons.predicate.NodeTypePredicate); importClass(org.apache.jackrabbit.spi.commons.iterator.Iterators); importClass(org.apache.jackrabbit.spi.commons.iterator.Predicate); var TMP_CAT_SUFFIX = '__TO_BE_MIGRATED' ; var IS_TAG_PREDICATE = new NodeTypePredicate( "ametys:tag" , true ); // Not working because ametys:tag-category is not defined as a nodetype anymore. // var IS_CATEGORY_PREDICATE = new NodeTypePredicate( "ametys:tag-category" , true ); var IS_CATEGORY_PREDICATE = new Predicate() { evaluate: function (node) { return node.getProperty( 'jcr:primaryType' ).getString() == 'ametys:tag-category' ; } } var ID_MAPPER = {}; var catCount = 0; var deductTagTarget = function (catNode) { // Filter on category children. var tagNodes = Iterators.filterIterator(Iterators.nodes(catNode.getNodes()), new Predicate() { evaluate: function (node) { return IS_TAG_PREDICATE.evaluate(node); } }); if (tagNodes.hasNext()) { var tagNode = tagNodes.next(); return tagNode.hasProperty( 'ametys-internal:target' ) ? tagNode.getProperty( 'ametys-internal:target' ).getString() : null ; } return null ; } var migrateCategory = function (catNode, s) { var parent = catNode.getParent(); var tagInfo = { name: StringUtils.removeEnd(catNode.getName(), TMP_CAT_SUFFIX), title: catNode.hasProperty( 'ametys-internal:title' ) ? catNode.getProperty( 'ametys-internal:title' ).getString() : 'Unknown title' , description: catNode.hasProperty( 'ametys-internal:description' ) ? catNode.getProperty( 'ametys-internal:description' ).getString() : 'Unknown description' , visibility: 'PUBLIC' , target: deductTagTarget(catNode) || 'CONTENT' }; // Add and populate a new tag node var tagNode = parent.addNode(tagInfo.name, 'ametys:tag' ); tagNode.setProperty( 'ametys-internal:title' , tagInfo.title); if (tagInfo.description) tagNode.setProperty( 'ametys-internal:description' , tagInfo.description); tagNode.setProperty( 'ametys-internal:visibility' , tagInfo.visibility); tagNode.setProperty( 'ametys-internal:target' , tagInfo.target); // Keep track of old identifer <-> new tag name to be able update service parameters below ID_MAPPER[catNode.getIdentifier()] = Text.unescapeIllegalJcrChars(tagNode.getName()); // Add category child nodes to the new tag. var childNodes = catNode.getNodes(); while (childNodes.hasNext()) { var childNode = childNodes.next(); s.move(childNode.getPath(), tagNode.getPath() + '/' + childNode.getName()); } // Remove tag category node. catNode.remove(); } var migrateCategories = function (parentNode, s) { // Filter on category children. var catNodes = Iterators.filterIterator(Iterators.nodes(parentNode.getNodes()), IS_CATEGORY_PREDICATE); while (catNodes.hasNext()) { var catNode = catNodes.next(); // Recursive preprocessing migrateCategories(catNode); if (StringUtils.endsWith(catNode.getName(), TMP_CAT_SUFFIX)) { migrateCategory(catNode, s); catCount++; } } } // 1 - Migrate categories... var migrateStep1 = function (s) { var qm = s.getWorkspace().getQueryManager(); var query = qm.createQuery( " //element(*, ametys:site)/ametys-internal:plugins/web/tags" , javax.jcr.query.Query.XPATH); var rootTagsNodes = query.execute().getNodes(); while (rootTagsNodes.hasNext()) { migrateCategories(rootTagsNodes.next(), s); } } migrateStep1(session); migrateStep1(liveSession); // 2 - Migrate impacted service parameters var serviceParamNames = { 'org.ametys.plugins.calendar.Agenda' : 'ametys:tag-categories' , 'org.ametys.plugins.tagcloud.services.Tags' : 'ametys:search-by-tags' , 'org.ametys.web.service.FrontSearchService' : 'ametys:search-by-tags' }; var serviceCount = 0; var serviceChange = false ; var migrateStep2 = function (s) { var qm = s.getWorkspace().getQueryManager(); var serviceQueryPart = "@ametys-internal:service= 'org.ametys.plugins.calendar.Agenda' or @ametys-internal:service= 'org.ametys.plugins.tagcloud.services.Tags' or @ametys-internal:service= 'org.ametys.web.service.FrontSearchService' " ; var query = qm.createQuery( " //element(*, ametys:zoneItem)[@ametys-internal:type= 'SERVICE' and (" + serviceQueryPart + ")]" , javax.jcr.query.Query.XPATH); var serviceNodes = query.execute().getNodes(); while (serviceNodes.hasNext()) { var serviceNode = serviceNodes.next(); var serviceId = serviceNode.getProperty( 'ametys-internal:service' ).getString(); var paramsNode = serviceNode.getNode( 'ametys:service_parameters' ); var paramName = serviceParamNames[serviceId]; serviceChange = false ; if (paramsNode.hasProperty(paramName)) { var values = StringUtils.split(paramsNode.getProperty(paramName).getString(), ',' ); if (values != null ) { for ( var idx in values) { var v = values[idx]; if (StringUtils.startsWith(v, "tag-category: //" )) { var id = StringUtils.removeStart(v, "tag-category: //" ); values[idx] = ID_MAPPER[id] || values[idx]; serviceChange = true ; } } // update service property paramsNode.setProperty(paramName, StringUtils.join(values, ',' )); } } if (serviceChange) { serviceCount++; } } } migrateStep2(session); migrateStep2(liveSession); session.save(); liveSession.save(); println( "Successfully migrated " + catCount + " tag categories." ); println( "Successfully migrated " + serviceCount + " impacted service." );

          Thibaut Rizzi (Inactive) added a comment - - edited

          Migration script part 1 (pre-processing) :

          importClass(org.apache.commons.lang.StringUtils);
          importClass(javax.jcr.NodeIterator);
          importClass(org.apache.jackrabbit.spi.commons.iterator.Iterators);
          importClass(org.apache.jackrabbit.spi.commons.iterator.Predicate);
          
          var TMP_CAT_SUFFIX = '__TO_BE_MIGRATED';
          // Not working because ametys:tag-category is not defined as a nodetype anymore.
          // var IS_CATEGORY_PREDICATE = new NodeTypePredicate("ametys:tag-category", true);
          var IS_CATEGORY_PREDICATE = new Predicate() {
            evaluate: function(node) {
                return node.getProperty('jcr:primaryType').getString() == 'ametys:tag-category';
              }
          }
          var catCount = 0;
          
          var preprocessCategories = function(parentNode, s)
          {
            // Filter on category children.
          	var catNodes = Iterators.filterIterator(Iterators.nodes(parentNode.getNodes()), IS_CATEGORY_PREDICATE);
          
          	while (catNodes.hasNext()) {
          		var catNode = catNodes.next();
          
          		// Recursive preprocessing
          		preprocessCategories(catNode);
          
          		// Renaming category node
          		var catNodeName = catNode.getName(),
                  newCatNodeName;
          
              if (!StringUtils.endsWith(catNodeName, TMP_CAT_SUFFIX))
              {
                newCatNodeName = catNode.getName().replaceAll('\\W', '_') + TMP_CAT_SUFFIX;
                s.move(catNode.getPath(), catNode.getParent().getPath() + '/' + newCatNodeName.toUpperCase());
                catCount++;
              }
            }
          }
          
          // Processing default workspace
          var qm = session.getWorkspace().getQueryManager();
          var query = qm.createQuery("//element(*, ametys:site)/ametys-internal:plugins/web/tags", javax.jcr.query.Query.XPATH);
          var rootTagsNodes = query.execute().getNodes();
          
          while (rootTagsNodes.hasNext())
          {
          	preprocessCategories(rootTagsNodes.next(), session);
          }
          
          // Processing live workspace
          qm = liveSession.getWorkspace().getQueryManager();
          query = qm.createQuery("//element(*, ametys:site)/ametys-internal:plugins/web/tags", javax.jcr.query.Query.XPATH);
          rootTagsNodes = query.execute().getNodes();
          
          while (rootTagsNodes.hasNext())
          {
            preprocessCategories(rootTagsNodes.next(), liveSession);
          }
          
          session.save();
          liveSession.save();
          
          println("Successfully pre-processed " + catCount + " tag categories.");
          

          Thibaut Rizzi (Inactive) added a comment - - edited Migration script part 1 (pre-processing) : importClass(org.apache.commons.lang.StringUtils); importClass(javax.jcr.NodeIterator); importClass(org.apache.jackrabbit.spi.commons.iterator.Iterators); importClass(org.apache.jackrabbit.spi.commons.iterator.Predicate); var TMP_CAT_SUFFIX = '__TO_BE_MIGRATED' ; // Not working because ametys:tag-category is not defined as a nodetype anymore. // var IS_CATEGORY_PREDICATE = new NodeTypePredicate( "ametys:tag-category" , true ); var IS_CATEGORY_PREDICATE = new Predicate() { evaluate: function (node) { return node.getProperty( 'jcr:primaryType' ).getString() == 'ametys:tag-category' ; } } var catCount = 0; var preprocessCategories = function (parentNode, s) { // Filter on category children. var catNodes = Iterators.filterIterator(Iterators.nodes(parentNode.getNodes()), IS_CATEGORY_PREDICATE); while (catNodes.hasNext()) { var catNode = catNodes.next(); // Recursive preprocessing preprocessCategories(catNode); // Renaming category node var catNodeName = catNode.getName(), newCatNodeName; if (!StringUtils.endsWith(catNodeName, TMP_CAT_SUFFIX)) { newCatNodeName = catNode.getName().replaceAll( '\\W' , '_' ) + TMP_CAT_SUFFIX; s.move(catNode.getPath(), catNode.getParent().getPath() + '/' + newCatNodeName.toUpperCase()); catCount++; } } } // Processing default workspace var qm = session.getWorkspace().getQueryManager(); var query = qm.createQuery( " //element(*, ametys:site)/ametys-internal:plugins/web/tags" , javax.jcr.query.Query.XPATH); var rootTagsNodes = query.execute().getNodes(); while (rootTagsNodes.hasNext()) { preprocessCategories(rootTagsNodes.next(), session); } // Processing live workspace qm = liveSession.getWorkspace().getQueryManager(); query = qm.createQuery( " //element(*, ametys:site)/ametys-internal:plugins/web/tags" , javax.jcr.query.Query.XPATH); rootTagsNodes = query.execute().getNodes(); while (rootTagsNodes.hasNext()) { preprocessCategories(rootTagsNodes.next(), liveSession); } session.save(); liveSession.save(); println( "Successfully pre-processed " + catCount + " tag categories." );

            trizzi Thibaut Rizzi (Inactive)
            trizzi Thibaut Rizzi (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Resolved: