Uploaded image for project: 'Jenkins'
  1. Jenkins
  2. JENKINS-74107

[dynamic_extended_choice_parameter] Extract inline script block and event handlers in com/moded/extendedchoiceparameter/ExtendedChoiceParameterDefinition/multiLevel.jelly

      Problems

      == Inline Script Block
      Line: 5
      ----
      <script type="text/javascript">
      
      	function ${it.name}RemoveSelectDiv(selectDiv)
      	{
      		var selectDivs = document.getElementsByClassName("${it.name} select div");
      		if (selectDivs.length == 1)
      		{
      			unhideNext${it.name}()
      		}
      
      		var parent = selectDiv.parentNode;
      		parent.removeChild(selectDiv);
      
      		onlyShowRemoveButtonsWhenMultiple${it.name}VisibleSelectionDivs();
      	}
      		
      	function onlyShowRemoveButtonsWhenMultiple${it.name}VisibleSelectionDivs()
      	{
      		// first determine whether or not there are multiple visible select div
      		// elements for ${it.name}
      		var visibleSelectDivs = 0;
      		var selectDivs = document.getElementsByClassName("${it.name} select div");
      		for (var i=0; i != selectDivs.length; i++)
      		{
      			visibleSelectDivs++;
      		}
      	
      		var removeButtonsForDiv = document.getElementsByClassName("${it.name} remove button");
      		for(var i=0; i != removeButtonsForDiv.length; i++)
      		{
      			removeButtonsForDiv[i].style.display = visibleSelectDivs > 1 ? "inline" : "none";
      		}
      	}
      	
      	function ${it.name}MultiLevelSelectNumber(selectNodeOrParentDiv)
      	{
      		var selectId = selectNodeOrParentDiv.getAttribute("id");
      		var indexOfNumber = selectId.indexOf("MultiLevelMultiSelect ");
      		return parseInt(selectId.substr(indexOfNumber).split(" ")[1]);
      	}
      	
      	function unhideNext${it.name}()
      	{	
      		var selectDivs = document.getElementsByClassName("${it.name} select div");
      		var lastSelect = selectDivs[selectDivs.length - 1];
      
      		var lastSelectNumber = ${it.name}MultiLevelSelectNumber(lastSelect);
      		
      		var lastMultiLevelMultiSelectDiv = document.getElementById(
      			"${it.name} dropdowns for MultiLevelMultiSelect " + lastSelectNumber);
      		
      		var nextMultiLevelMultiSelectDiv = lastMultiLevelMultiSelectDiv.cloneNode(true);
      
      		nextMultiLevelMultiSelectDiv.setAttribute("id", 
      			"${it.name} dropdowns for MultiLevelMultiSelect " + (lastSelectNumber + 1));
      
      		var nextDropdown = nextMultiLevelMultiSelectDiv.firstChild;
      		var hiddenNextSelectionIdMadeVisible = false;
      		while (nextDropdown)
      		{		
      			var id = nextDropdown.getAttribute("id");
      			if (id !== null)
      			{
      				nextDropdown.style.display = "none";
      
      				id = id.replace("dropdown MultiLevelMultiSelect " + lastSelectNumber,
      					"dropdown MultiLevelMultiSelect " + (lastSelectNumber + 1));
      				nextDropdown.setAttribute("id", id);
      
      				if (hiddenNextSelectionIdMadeVisible === false)
      				{
      					nextDropdown.style.display = "inline";
      					hiddenNextSelectionIdMadeVisible = true;
      				}
      			}
      
      			if (nextDropdown.name === "value")
      			{
      				nextDropdown.name = "tmp name";
      			}
      
      			nextDropdown = nextDropdown.nextSibling;
      		}
      
      		var multiLevelSelections = document.getElementById("${it.name} MultiLevelSelections");
      
      		multiLevelSelections.appendChild(nextMultiLevelMultiSelectDiv);
      
      		onlyShowRemoveButtonsWhenMultiple${it.name}VisibleSelectionDivs();
      	}
      
      	function ${it.name}Changed(select)
      	{
      		var currentSelectNumber = ${it.name}MultiLevelSelectNumber(select);
      
      		var currentMultiLevelMultiSelectDiv = document.getElementById(
      			"${it.name} dropdowns for MultiLevelMultiSelect " + currentSelectNumber);
      
      		var dropdown = currentMultiLevelMultiSelectDiv.firstChild;
      		while (dropdown)
      		{
      			var dropdownId = dropdown.getAttribute("id");
      			if (dropdownId !== null)
      			{
      				var dropdownShouldNotBeDisplayed =
      					select.getAttribute("id").indexOf(dropdownId) == -1;
      
      				if (dropdownShouldNotBeDisplayed)
      				{
      					// this allows a user to got back and choose a different selection
      					dropdown.style.display = "none";
      					dropdown.name = "tmp name";
      				}
      			}
      			dropdown = dropdown.nextSibling;
      		}
      
      		select.name = "value";
      		var selectedItem = select.options[select.selectedIndex].value;
      		var nextDropdown = document.getElementById(select.id + " " + selectedItem);
      		if (nextDropdown)
      		{
      			nextDropdown.style.display = "inline";
      			
      			// in case this was previously selected from, then made invisible, and then made 
      			// visible again, we need to set the first case as selected
      
      			nextDropdown.options[0].selected = true;
      		}
      		else
      		{
      			<j:if test="${type eq 'PT_MULTI_LEVEL_MULTI_SELECT'}">
      				var addAnotherButton = document.getElementById("${it.name} addAnotherButton");
      				addAnotherButton.style.display = "inline";
      				var addAnotherButtonLineBreak =
      					document.getElementById("${it.name} addAnotherButton linebreak");
      				addAnotherButtonLineBreak.style.display = "inline";
      			</j:if>
      		}
      	}
      </script>
      ----
      
      == Inline Event Handler
      Line: 160
      ----
      <select name="tmp name" onchange="${it.name}Changed(this, '${it.name}')"
      					id="${dropdown}" style="${style}">
      ----
      
      == Inline Event Handler
      Line: 168
      ----
      <input class="${it.name} remove button" type="button" value="Remove" 
      				style="display:none" onclick="${it.name}RemoveSelectDiv(this.parentNode)"/>
      ----
      
      == Inline Event Handler
      Line: 172
      ----
      <input id="${it.name} addAnotherButton" type="button" value="Select another..."
      		style="display:none" onclick="unhideNext${it.name}();"/>
      ----
      

      Solutions

      https://www.jenkins.io/doc/developer/security/csp/#inline-javascript-blocks
      https://www.jenkins.io/doc/developer/security/csp/#inline-event-handlers

          [JENKINS-74107] [dynamic_extended_choice_parameter] Extract inline script block and event handlers in com/moded/extendedchoiceparameter/ExtendedChoiceParameterDefinition/multiLevel.jelly

          Basil Crow created issue -
          Basil Crow made changes -
          Summary Original: [dynamic_extended_choice_parameter] Extract inline script blocks and lsevent handlers in com/moded/extendedchoiceparameter/ExtendedChoiceParameterDefinition/multiLevel.jelly New: [dynamic_extended_choice_parameter] Extract inline script blocks and event handlers in com/moded/extendedchoiceparameter/ExtendedChoiceParameterDefinition/multiLevel.jelly
          Basil Crow made changes -
          Link New: This issue is duplicated by JENKINS-73957 [ JENKINS-73957 ]
          Basil Crow made changes -
          Description Original: h4. Problems

          {noformat}
          == Inline Script Block
          Line: 5
          ----
          <script type="text/javascript">

          function ${it.name}RemoveSelectDiv(selectDiv)
          {
          var selectDivs = document.getElementsByClassName("${it.name} select div");
          if (selectDivs.length == 1)
          {
          unhideNext${it.name}()
          }

          var parent = selectDiv.parentNode;
          parent.removeChild(selectDiv);

          onlyShowRemoveButtonsWhenMultiple${it.name}VisibleSelectionDivs();
          }

          function onlyShowRemoveButtonsWhenMultiple${it.name}VisibleSelectionDivs()
          {
          // first determine whether or not there are multiple visible select div
          // elements for ${it.name}
          var visibleSelectDivs = 0;
          var selectDivs = document.getElementsByClassName("${it.name} select div");
          for (var i=0; i != selectDivs.length; i++)
          {
          visibleSelectDivs++;
          }

          var removeButtonsForDiv = document.getElementsByClassName("${it.name} remove button");
          for(var i=0; i != removeButtonsForDiv.length; i++)
          {
          removeButtonsForDiv[i].style.display = visibleSelectDivs > 1 ? "inline" : "none";
          }
          }

          function ${it.name}MultiLevelSelectNumber(selectNodeOrParentDiv)
          {
          var selectId = selectNodeOrParentDiv.getAttribute("id");
          var indexOfNumber = selectId.indexOf("MultiLevelMultiSelect ");
          return parseInt(selectId.substr(indexOfNumber).split(" ")[1]);
          }

          function unhideNext${it.name}()
          {
          var selectDivs = document.getElementsByClassName("${it.name} select div");
          var lastSelect = selectDivs[selectDivs.length - 1];

          var lastSelectNumber = ${it.name}MultiLevelSelectNumber(lastSelect);

          var lastMultiLevelMultiSelectDiv = document.getElementById(
          "${it.name} dropdowns for MultiLevelMultiSelect " + lastSelectNumber);

          var nextMultiLevelMultiSelectDiv = lastMultiLevelMultiSelectDiv.cloneNode(true);

          nextMultiLevelMultiSelectDiv.setAttribute("id",
          "${it.name} dropdowns for MultiLevelMultiSelect " + (lastSelectNumber + 1));

          var nextDropdown = nextMultiLevelMultiSelectDiv.firstChild;
          var hiddenNextSelectionIdMadeVisible = false;
          while (nextDropdown)
          {
          var id = nextDropdown.getAttribute("id");
          if (id !== null)
          {
          nextDropdown.style.display = "none";

          id = id.replace("dropdown MultiLevelMultiSelect " + lastSelectNumber,
          "dropdown MultiLevelMultiSelect " + (lastSelectNumber + 1));
          nextDropdown.setAttribute("id", id);

          if (hiddenNextSelectionIdMadeVisible === false)
          {
          nextDropdown.style.display = "inline";
          hiddenNextSelectionIdMadeVisible = true;
          }
          }

          if (nextDropdown.name === "value")
          {
          nextDropdown.name = "tmp name";
          }

          nextDropdown = nextDropdown.nextSibling;
          }

          var multiLevelSelections = document.getElementById("${it.name} MultiLevelSelections");

          multiLevelSelections.appendChild(nextMultiLevelMultiSelectDiv);

          onlyShowRemoveButtonsWhenMultiple${it.name}VisibleSelectionDivs();
          }

          function ${it.name}Changed(select)
          {
          var currentSelectNumber = ${it.name}MultiLevelSelectNumber(select);

          var currentMultiLevelMultiSelectDiv = document.getElementById(
          "${it.name} dropdowns for MultiLevelMultiSelect " + currentSelectNumber);

          var dropdown = currentMultiLevelMultiSelectDiv.firstChild;
          while (dropdown)
          {
          var dropdownId = dropdown.getAttribute("id");
          if (dropdownId !== null)
          {
          var dropdownShouldNotBeDisplayed =
          select.getAttribute("id").indexOf(dropdownId) == -1;

          if (dropdownShouldNotBeDisplayed)
          {
          // this allows a user to got back and choose a different selection
          dropdown.style.display = "none";
          dropdown.name = "tmp name";
          }
          }
          dropdown = dropdown.nextSibling;
          }

          select.name = "value";
          var selectedItem = select.options[select.selectedIndex].value;
          var nextDropdown = document.getElementById(select.id + " " + selectedItem);
          if (nextDropdown)
          {
          nextDropdown.style.display = "inline";

          // in case this was previously selected from, then made invisible, and then made
          // visible again, we need to set the first case as selected

          nextDropdown.options[0].selected = true;
          }
          else
          {
          <j:if test="${type eq 'PT_MULTI_LEVEL_MULTI_SELECT'}">
          var addAnotherButton = document.getElementById("${it.name} addAnotherButton");
          addAnotherButton.style.display = "inline";
          var addAnotherButtonLineBreak =
          document.getElementById("${it.name} addAnotherButton linebreak");
          addAnotherButtonLineBreak.style.display = "inline";
          </j:if>
          }
          }
          </script>
          ----

          == Inline Event Handler
          Line: 160
          ----
          <select name="tmp name" onchange="${it.name}Changed(this, '${it.name}')"
          id="${dropdown}" style="${style}">
          ----

          == Inline Event Handler
          Line: 168
          ----
          <input class="${it.name} remove button" type="button" value="Remove"
          style="display:none" onclick="${it.name}RemoveSelectDiv(this.parentNode)"/>
          ----

          == Inline Event Handler
          Line: 172
          ----
          <input id="${it.name} addAnotherButton" type="button" value="Select another..."
          style="display:none" onclick="unhideNext${it.name}();"/>
          ----

          == Inline Script Block
          Line: 5
          ----
          <script type="text/javascript">

          function ${it.name}RemoveSelectDiv(selectDiv)
          {
          var selectDivs = document.getElementsByClassName("${it.name} select div");
          if (selectDivs.length == 1)
          {
          unhideNext${it.name}()
          }

          var parent = selectDiv.parentNode;
          parent.removeChild(selectDiv);

          onlyShowRemoveButtonsWhenMultiple${it.name}VisibleSelectionDivs();
          }

          function onlyShowRemoveButtonsWhenMultiple${it.name}VisibleSelectionDivs()
          {
          // first determine whether or not there are multiple visible select div
          // elements for ${it.name}
          var visibleSelectDivs = 0;
          var selectDivs = document.getElementsByClassName("${it.name} select div");
          for (var i=0; i != selectDivs.length; i++)
          {
          visibleSelectDivs++;
          }

          var removeButtonsForDiv = document.getElementsByClassName("${it.name} remove button");
          for(var i=0; i != removeButtonsForDiv.length; i++)
          {
          removeButtonsForDiv[i].style.display = visibleSelectDivs > 1 ? "inline" : "none";
          }
          }

          function ${it.name}MultiLevelSelectNumber(selectNodeOrParentDiv)
          {
          var selectId = selectNodeOrParentDiv.getAttribute("id");
          var indexOfNumber = selectId.indexOf("MultiLevelMultiSelect ");
          return parseInt(selectId.substr(indexOfNumber).split(" ")[1]);
          }

          function unhideNext${it.name}()
          {
          var selectDivs = document.getElementsByClassName("${it.name} select div");
          var lastSelect = selectDivs[selectDivs.length - 1];

          var lastSelectNumber = ${it.name}MultiLevelSelectNumber(lastSelect);

          var lastMultiLevelMultiSelectDiv = document.getElementById(
          "${it.name} dropdowns for MultiLevelMultiSelect " + lastSelectNumber);

          var nextMultiLevelMultiSelectDiv = lastMultiLevelMultiSelectDiv.cloneNode(true);

          nextMultiLevelMultiSelectDiv.setAttribute("id",
          "${it.name} dropdowns for MultiLevelMultiSelect " + (lastSelectNumber + 1));

          var nextDropdown = nextMultiLevelMultiSelectDiv.firstChild;
          var hiddenNextSelectionIdMadeVisible = false;
          while (nextDropdown)
          {
          var id = nextDropdown.getAttribute("id");
          if (id !== null)
          {
          nextDropdown.style.display = "none";

          id = id.replace("dropdown MultiLevelMultiSelect " + lastSelectNumber,
          "dropdown MultiLevelMultiSelect " + (lastSelectNumber + 1));
          nextDropdown.setAttribute("id", id);

          if (hiddenNextSelectionIdMadeVisible === false)
          {
          nextDropdown.style.display = "inline";
          hiddenNextSelectionIdMadeVisible = true;
          }
          }

          if (nextDropdown.name === "value")
          {
          nextDropdown.name = "tmp name";
          }

          nextDropdown = nextDropdown.nextSibling;
          }

          var multiLevelSelections = document.getElementById("${it.name} MultiLevelSelections");

          multiLevelSelections.appendChild(nextMultiLevelMultiSelectDiv);

          onlyShowRemoveButtonsWhenMultiple${it.name}VisibleSelectionDivs();
          }

          function ${it.name}Changed(select)
          {
          var currentSelectNumber = ${it.name}MultiLevelSelectNumber(select);

          var currentMultiLevelMultiSelectDiv = document.getElementById(
          "${it.name} dropdowns for MultiLevelMultiSelect " + currentSelectNumber);

          var dropdown = currentMultiLevelMultiSelectDiv.firstChild;
          while (dropdown)
          {
          var dropdownId = dropdown.getAttribute("id");
          if (dropdownId !== null)
          {
          var dropdownShouldNotBeDisplayed =
          select.getAttribute("id").indexOf(dropdownId) == -1;

          if (dropdownShouldNotBeDisplayed)
          {
          // this allows a user to got back and choose a different selection
          dropdown.style.display = "none";
          dropdown.name = "tmp name";
          }
          }
          dropdown = dropdown.nextSibling;
          }

          select.name = "value";
          var selectedItem = select.options[select.selectedIndex].value;
          var nextDropdown = document.getElementById(select.id + " " + selectedItem);
          if (nextDropdown)
          {
          nextDropdown.style.display = "inline";

          // in case this was previously selected from, then made invisible, and then made
          // visible again, we need to set the first case as selected

          nextDropdown.options[0].selected = true;
          }
          else
          {
          <j:if test="${type eq 'PT_MULTI_LEVEL_MULTI_SELECT'}">
          var addAnotherButton = document.getElementById("${it.name} addAnotherButton");
          addAnotherButton.style.display = "inline";
          var addAnotherButtonLineBreak =
          document.getElementById("${it.name} addAnotherButton linebreak");
          addAnotherButtonLineBreak.style.display = "inline";
          </j:if>
          }
          }
          </script>
          ----

          == Inline Event Handler
          Line: 160
          ----
          <select name="tmp name" onchange="${it.name}Changed(this, '${it.name}')"
          id="${dropdown}" style="${style}">
          ----

          == Inline Event Handler
          Line: 168
          ----
          <input class="${it.name} remove button" type="button" value="Remove"
          style="display:none" onclick="${it.name}RemoveSelectDiv(this.parentNode)"/>
          ----

          == Inline Event Handler
          Line: 172
          ----
          <input id="${it.name} addAnotherButton" type="button" value="Select another..."
          style="display:none" onclick="unhideNext${it.name}();"/>
          ----
          {noformat}

          h4. Solutions

          [https://www.jenkins.io/doc/developer/security/csp/#inline-javascript-blocks]
          [https://www.jenkins.io/doc/developer/security/csp/#inline-event-handlers]
          New: h4. Problems

          {noformat}
          == Inline Script Block
          Line: 5
          ----
          <script type="text/javascript">

          function ${it.name}RemoveSelectDiv(selectDiv)
          {
          var selectDivs = document.getElementsByClassName("${it.name} select div");
          if (selectDivs.length == 1)
          {
          unhideNext${it.name}()
          }

          var parent = selectDiv.parentNode;
          parent.removeChild(selectDiv);

          onlyShowRemoveButtonsWhenMultiple${it.name}VisibleSelectionDivs();
          }

          function onlyShowRemoveButtonsWhenMultiple${it.name}VisibleSelectionDivs()
          {
          // first determine whether or not there are multiple visible select div
          // elements for ${it.name}
          var visibleSelectDivs = 0;
          var selectDivs = document.getElementsByClassName("${it.name} select div");
          for (var i=0; i != selectDivs.length; i++)
          {
          visibleSelectDivs++;
          }

          var removeButtonsForDiv = document.getElementsByClassName("${it.name} remove button");
          for(var i=0; i != removeButtonsForDiv.length; i++)
          {
          removeButtonsForDiv[i].style.display = visibleSelectDivs > 1 ? "inline" : "none";
          }
          }

          function ${it.name}MultiLevelSelectNumber(selectNodeOrParentDiv)
          {
          var selectId = selectNodeOrParentDiv.getAttribute("id");
          var indexOfNumber = selectId.indexOf("MultiLevelMultiSelect ");
          return parseInt(selectId.substr(indexOfNumber).split(" ")[1]);
          }

          function unhideNext${it.name}()
          {
          var selectDivs = document.getElementsByClassName("${it.name} select div");
          var lastSelect = selectDivs[selectDivs.length - 1];

          var lastSelectNumber = ${it.name}MultiLevelSelectNumber(lastSelect);

          var lastMultiLevelMultiSelectDiv = document.getElementById(
          "${it.name} dropdowns for MultiLevelMultiSelect " + lastSelectNumber);

          var nextMultiLevelMultiSelectDiv = lastMultiLevelMultiSelectDiv.cloneNode(true);

          nextMultiLevelMultiSelectDiv.setAttribute("id",
          "${it.name} dropdowns for MultiLevelMultiSelect " + (lastSelectNumber + 1));

          var nextDropdown = nextMultiLevelMultiSelectDiv.firstChild;
          var hiddenNextSelectionIdMadeVisible = false;
          while (nextDropdown)
          {
          var id = nextDropdown.getAttribute("id");
          if (id !== null)
          {
          nextDropdown.style.display = "none";

          id = id.replace("dropdown MultiLevelMultiSelect " + lastSelectNumber,
          "dropdown MultiLevelMultiSelect " + (lastSelectNumber + 1));
          nextDropdown.setAttribute("id", id);

          if (hiddenNextSelectionIdMadeVisible === false)
          {
          nextDropdown.style.display = "inline";
          hiddenNextSelectionIdMadeVisible = true;
          }
          }

          if (nextDropdown.name === "value")
          {
          nextDropdown.name = "tmp name";
          }

          nextDropdown = nextDropdown.nextSibling;
          }

          var multiLevelSelections = document.getElementById("${it.name} MultiLevelSelections");

          multiLevelSelections.appendChild(nextMultiLevelMultiSelectDiv);

          onlyShowRemoveButtonsWhenMultiple${it.name}VisibleSelectionDivs();
          }

          function ${it.name}Changed(select)
          {
          var currentSelectNumber = ${it.name}MultiLevelSelectNumber(select);

          var currentMultiLevelMultiSelectDiv = document.getElementById(
          "${it.name} dropdowns for MultiLevelMultiSelect " + currentSelectNumber);

          var dropdown = currentMultiLevelMultiSelectDiv.firstChild;
          while (dropdown)
          {
          var dropdownId = dropdown.getAttribute("id");
          if (dropdownId !== null)
          {
          var dropdownShouldNotBeDisplayed =
          select.getAttribute("id").indexOf(dropdownId) == -1;

          if (dropdownShouldNotBeDisplayed)
          {
          // this allows a user to got back and choose a different selection
          dropdown.style.display = "none";
          dropdown.name = "tmp name";
          }
          }
          dropdown = dropdown.nextSibling;
          }

          select.name = "value";
          var selectedItem = select.options[select.selectedIndex].value;
          var nextDropdown = document.getElementById(select.id + " " + selectedItem);
          if (nextDropdown)
          {
          nextDropdown.style.display = "inline";

          // in case this was previously selected from, then made invisible, and then made
          // visible again, we need to set the first case as selected

          nextDropdown.options[0].selected = true;
          }
          else
          {
          <j:if test="${type eq 'PT_MULTI_LEVEL_MULTI_SELECT'}">
          var addAnotherButton = document.getElementById("${it.name} addAnotherButton");
          addAnotherButton.style.display = "inline";
          var addAnotherButtonLineBreak =
          document.getElementById("${it.name} addAnotherButton linebreak");
          addAnotherButtonLineBreak.style.display = "inline";
          </j:if>
          }
          }
          </script>
          ----

          == Inline Event Handler
          Line: 160
          ----
          <select name="tmp name" onchange="${it.name}Changed(this, '${it.name}')"
          id="${dropdown}" style="${style}">
          ----

          == Inline Event Handler
          Line: 168
          ----
          <input class="${it.name} remove button" type="button" value="Remove"
          style="display:none" onclick="${it.name}RemoveSelectDiv(this.parentNode)"/>
          ----

          == Inline Event Handler
          Line: 172
          ----
          <input id="${it.name} addAnotherButton" type="button" value="Select another..."
          style="display:none" onclick="unhideNext${it.name}();"/>
          ----
          {noformat}

          h4. Solutions

          [https://www.jenkins.io/doc/developer/security/csp/#inline-javascript-blocks]
          [https://www.jenkins.io/doc/developer/security/csp/#inline-event-handlers]
          Summary Original: [dynamic_extended_choice_parameter] Extract inline script blocks and event handlers in com/moded/extendedchoiceparameter/ExtendedChoiceParameterDefinition/multiLevel.jelly New: [dynamic_extended_choice_parameter] Extract inline script block and event handlers in com/moded/extendedchoiceparameter/ExtendedChoiceParameterDefinition/multiLevel.jelly
          Basil Crow made changes -
          Link Original: This issue is duplicated by JENKINS-73957 [ JENKINS-73957 ]
          Yaroslav Afenkin made changes -
          Assignee New: Yaroslav Afenkin [ yafenkin ]

            yafenkin Yaroslav Afenkin
            basil Basil Crow
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated: