I want to add repeatable properties to the Jenkins plugin I'm developing, and
created a test plugin to make make sure I was using them correctly. My
plugin seems to work fine, I can add as many properties as I want when I
originally edit the config, and it saves and builds. However, when I try to
edit the config a second time, the config screen shows the loading overlay
endlessly. If I scroll down, I can see the properties I saved earlier are
still there, but I can't edit anything.

My class looks like this:

public class RepeatableTest extends Builder {

    private List<Prop> property = new ArrayList<Prop>();


    @DataBoundConstructor
    public RepeatableTest(List<Prop> property) {
        this.property = property;
    }

    public List<Prop> getProperty() {
        return property;
    }

    @Override
    public boolean perform(AbstractBuild build, Launcher launcher,
BuildListener listener) throws IOException {
        listener.getLogger().println(property.get(0).name);
        listener.getLogger().println(property.size());
        return true;
    }

    @Override
    public DescriptorImpl getDescriptor() {
        return (DescriptorImpl)super.getDescriptor();
    }

    public static class Prop extends AbstractDescribableImpl<Prop> {
        public String name;

        public String getName(){
            return name;
        }

        @DataBoundConstructor
        public Prop(String name) {
            this.name = name;
        }

        @Extension
        public static class DescriptorImpl extends Descriptor<Prop> {
            @Override
            public String getDisplayName() {
                return "";
            }
        }
    }

    @Extension // This indicates to Jenkins that this is an implementation
of an extension point.
    public static final class DescriptorImpl extends
BuildStepDescriptor<Builder> {

        private String phpLoc;

        public DescriptorImpl() {
            load();
        }

        public boolean isApplicable(Class<? extends AbstractProject> aClass)
{
            // Indicates that this builder can be used with all kinds of
project types 
            return true;
        }

        public String getDisplayName() {
            return "Repeatable Test";
        }

        @Override
        public boolean configure(StaplerRequest req, JSONObject formData)
throws FormException {
            phpLoc = formData.getString("phpLoc");
            save();
            return super.configure(req,formData);
        }

        public String getPhpLoc() {
            return phpLoc;
        }
    }
}
My config.groovy looks like this:

package uitestplugin.uitest.RepeatableTest;

import lib.JenkinsTagLib
import lib.FormTagLib

def f = namespace(lib.FormTagLib)
t=namespace(JenkinsTagLib.class)

f.form{
    f.entry(title:"Properties"){
        f.repeatableProperty(field:"property")
    }
}
and my prop/config.groovy looks like this:

package uitestplugin.uitest.RepeatableTest.Prop;

def f = namespace(lib.FormTagLib)

f.entry(title:"Name", field:"name") {
    f.textbox()
}
The config.xml:

<?xml version='1.0' encoding='UTF-8'?>
<project>
  <actions/>
  <description></description>
  <keepDependencies>false</keepDependencies>
  <properties/>
  <scm class="hudson.scm.NullSCM"/>
  <canRoam>true</canRoam>
  <disabled>false</disabled>
  <blockBuildWhenDownstreamBuilding>false</blockBuildWhenDownstreamBuilding>
  <blockBuildWhenUpstreamBuilding>false</blockBuildWhenUpstreamBuilding>
  <triggers/>
  <concurrentBuild>false</concurrentBuild>
  <builders>
    <uitestplugin.uitest.RepeatableTest plugin="ui-test@1.0-SNAPSHOT">
      <property>
        <uitestplugin.uitest.RepeatableTest_-Prop>
          <name>Prop1</name>
        </uitestplugin.uitest.RepeatableTest_-Prop>
        <uitestplugin.uitest.RepeatableTest_-Prop>
          <name>Prop2</name>
        </uitestplugin.uitest.RepeatableTest_-Prop>
      </property>
    </uitestplugin.uitest.RepeatableTest>
  </builders>
  <publishers/>
  <buildWrappers/>
</project>
Any ideas as to what could cause this? I based a lot of the code from the
ui-samples plugin
(https://wiki.jenkins-ci.org/display/JENKINS/UI+Samples+Plugin).

Also, using the web developer toolbar for Firefox, I can see that there is a
Javascript error on the page. The error is:

Timestamp: 10/3/2014 12:58:49 PM
Error: TypeError: prototypes is undefined
Source File:
http://localhost:8080/adjuncts/e58fb488/lib/form/hetero-list/hetero-list.js
Line: 16

And the code this relates to is(I've marked line 16 with a comment at the
end of the line):

// @include lib.form.dragdrop.dragdrop

// do the ones that extract innerHTML so that they can get their original
HTML before
// other behavior rules change them (like YUI buttons.)
Behaviour.specify("DIV.hetero-list-container", 'hetero-list', -100,
function(e) {
        e=$(e);
        if(isInsideRemovable(e))    return;

        // components for the add button
        var menu = document.createElement("SELECT");
        var btns = findElementsBySelector(e,"INPUT.hetero-list-add"),
            btn = btns[btns.length-1]; // In case nested content also uses
hetero-list
        YAHOO.util.Dom.insertAfter(menu,btn);

        var prototypes = $(e.lastChild);
        while(!prototypes.hasClassName("prototypes")) //LINE 16, ERROR IS
HERE
            prototypes = prototypes.previous();
        var insertionPoint = prototypes.previous();    // this is where the
new item is inserted.

        // extract templates
        var templates = []; var i=0;
        $(prototypes).childElements().each(function (n) {
            var name = n.getAttribute("name");
            var tooltip = n.getAttribute("tooltip");
            var descriptorId = n.getAttribute("descriptorId");
            menu.options[i] = new Option(n.getAttribute("title"),""+i);
            templates.push({html:n.innerHTML, name:name,
tooltip:tooltip,descriptorId:descriptorId});
            i++;
        });
        Element.remove(prototypes);

        var withDragDrop = initContainerDD(e);

        var menuAlign = (btn.getAttribute("menualign")||"tl-bl");

        var menuButton = new YAHOO.widget.Button(btn, { type: "menu", menu:
menu, menualignment: menuAlign.split("-") });
        $(menuButton._button).addClassName(btn.className);    // copy class
names
       
$(menuButton._button).setAttribute("suffix",btn.getAttribute("suffix"));
        menuButton.getMenu().clickEvent.subscribe(function(type,args,value)
{
            var item = args[1];
            if (item.cfg.getProperty("disabled"))   return;
            var t = templates[parseInt(item.value)];

            var nc = document.createElement("div");
            nc.className = "repeated-chunk";
            nc.setAttribute("name",t.name);
            nc.setAttribute("descriptorId",t.descriptorId);
            nc.innerHTML = t.html;
            $(nc).setOpacity(0);

            var scroll = document.body.scrollTop;

           
renderOnDemand(findElementsBySelector(nc,"TR.config-page")[0],function() {
                function findInsertionPoint() {
                    // given the element to be inserted 'prospect',
                    // and the array of existing items 'current',
                    // and preferred ordering function, return the position
in the array
                    // the prospect should be inserted.
                    // (for example 0 if it should be the first item)
                    function findBestPosition(prospect,current,order) {
                        function desirability(pos) {
                            var count=0;
                            for (var i=0; i<current.length; i++) {
                                if ((i<pos) ==
(order(current[i])<=order(prospect)))
                                    count++;
                            }
                            return count;
                        }

                        var bestScore = -1;
                        var bestPos = 0;
                        for (var i=0; i<=current.length; i++) {
                            var d = desirability(i);
                            if (bestScore<=d) {// prefer to insert them
toward the end
                                bestScore = d;
                                bestPos = i;
                            }
                        }
                        return bestPos;
                    }

                    var current = e.childElements().findAll(function(e)
{return e.match("DIV.repeated-chunk")});

                    function o(did) {
                        if (Object.isElement(did))
                            did = did.getAttribute("descriptorId");
                        for (var i=0; i<templates.length; i++)
                            if (templates[i].descriptorId==did)
                                return i;
                        return 0; // can't happen
                    }

                    var bestPos = findBestPosition(t.descriptorId, current,
o);
                    if (bestPos<current.length)
                        return current[bestPos];
                    else
                        return insertionPoint;
                }
                (e.hasClassName("honor-order") ? findInsertionPoint() :
insertionPoint).insert({before:nc});

                if(withDragDrop)    prepareDD(nc);

                new YAHOO.util.Anim(nc, {
                    opacity: { to:1 }
                }, 0.2, YAHOO.util.Easing.easeIn).animate();

                Behaviour.applySubtree(nc,true);
                ensureVisible(nc);
                layoutUpdateCallback.call();
            },true);
        });

        menuButton.getMenu().renderEvent.subscribe(function() {
            // hook up tooltip for menu items
            var items = menuButton.getMenu().getItems();
            for(i=0; i<items.length; i++) {
                var t = templates[i].tooltip;
                if(t!=null)
                    applyTooltip(items[i].element,t);
            }
        });

        if (e.hasClassName("one-each")) {
            // does this container already has a ocnfigured instance of the
specified descriptor ID?
            function has(id) {
                return
Prototype.Selector.find(e.childElements(),"DIV.repeated-chunk[descriptorId=\""+id+"\"]")!=null;
            }

            menuButton.getMenu().showEvent.subscribe(function() {
                var items = menuButton.getMenu().getItems();
                for(i=0; i<items.length; i++) {
                   
items[i].cfg.setProperty("disabled",has(templates[i].descriptorId));
                }
            });
        }
    });

Behaviour.specify("DIV.dd-handle", 'hetero-list', -100, function(e) {
        e=$(e);
        e.on("mouseover",function() {
            $(this).up(".repeated-chunk").addClassName("hover");
        });
        e.on("mouseout",function() {
            $(this).up(".repeated-chunk").removeClassName("hover");
        });
});



--
View this message in context: 
http://jenkins-ci.361315.n4.nabble.com/repeatableProperty-with-Groovy-Loading-overlay-not-dissapearing-on-config-screen-tp4722513.html
Sent from the Jenkins dev mailing list archive at Nabble.com.

-- 
You received this message because you are subscribed to the Google Groups 
"Jenkins Developers" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to jenkinsci-dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to