Package org.olat.course.condition

Source Code of org.olat.course.condition.AttributeEasyRowAdderController

/**
* OLAT - Online Learning and Training<br>
* http://www.olat.org
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at
* <p>
* http://www.apache.org/licenses/LICENSE-2.0
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br>
* University of Zurich, Switzerland.
* <p>
*/
package org.olat.course.condition;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Level;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.form.flexible.FormItem;
import org.olat.core.gui.components.form.flexible.FormItemContainer;
import org.olat.core.gui.components.form.flexible.elements.SingleSelection;
import org.olat.core.gui.components.form.flexible.elements.TextElement;
import org.olat.core.gui.components.form.flexible.impl.Form;
import org.olat.core.gui.components.form.flexible.impl.FormBasicController;
import org.olat.core.gui.components.form.flexible.impl.FormEvent;
import org.olat.core.gui.components.form.flexible.impl.FormLayoutContainer;
import org.olat.core.gui.components.form.flexible.impl.elements.FormLinkImpl;
import org.olat.core.gui.components.link.Link;
import org.olat.core.gui.control.Controller;
import org.olat.core.gui.control.Event;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.gui.translator.PackageTranslator;
import org.olat.core.gui.translator.Translator;
import org.olat.core.logging.AssertException;
import org.olat.core.logging.activity.LogModule;
import org.olat.core.util.ArrayHelper;
import org.olat.core.util.Util;
import org.olat.course.condition.operators.OperatorManager;
import org.olat.shibboleth.ShibbolethModule;
import org.olat.shibboleth.util.AttributeTranslator;
import org.olat.user.UserManager;

/**
* Description:<br>
* A subform that implement the shibboleth easy mode config rows.
*
* <P>
* Initial Date:  23.10.2006 <br>
* @author Lars Eberle (<a href="http://www.bps-system.de/">BPS Bildungsportal Sachsen GmbH</a>)
* @author Florian Gnägi (<a href="http://www.frentix.com/">frentix GmbH</a>)
*/
public class AttributeEasyRowAdderController extends FormBasicController {

  // the attribute name form elements
  private ArrayList<String> columnAttribute;
  // the operator form elements
  private ArrayList<String> columnOperator;
  // the value text input field form elements
  private ArrayList<String> columnValueText;
  // the alternative value selection drop down form elements
  private ArrayList<String> columnValueSelection;
  // add and remove row elements
  private ArrayList<String> columnAddRow;
  private ArrayList<String> columnRemoveRow;
  // identifyer prefixes
  private final static String PRE_ATTRIBUTE = "attribute_";
  private final static String PRE_OPERATOR = "operator_";
  private final static String PRE_VALUE_TEXT = "valuetxt_";
  private final static String PRE_VALUE_SELECTION = "valuesel_";
  // global row increment counter
  private int rowCreationCounter = 0;
  //
  private boolean isinit = false;
  private final String[] attrKeys;
  private final AttributeTranslator attributeTranslator;
  private String[] operatorKeys;

  /**
   * Constructor for a shibboleth attribute rule creator form.
   * @param ureq
   * @param wControl
   * @param parentForm
   */
  public AttributeEasyRowAdderController(UserRequest ureq, WindowControl wControl, Form parentForm) {
    super(ureq, wControl, FormBasicController.LAYOUT_CUSTOM, "easyrows", parentForm);
    // Set custom translator to use translations from shibb module as well
    setTranslator(Util.createPackageTranslator(ShibbolethModule.class, ureq.getLocale(), getTranslator()));
    attributeTranslator = ShibbolethModule.getAttributeTranslator();
    attrKeys = getShibAttributes();
    operatorKeys = OperatorManager.getRegisteredOperatorKeys(ShibbolethModule.getOperatorKeys());
    this.init();
  }
 
  /**
   * Constructor for a log attribute rule creator form.
   * @param ureq
   * @param wControl
   * @param parentForm
   */
  public AttributeEasyRowAdderController(UserRequest ureq, WindowControl wControl, Form parentForm, String[] attributes) {
    super(ureq, wControl, FormBasicController.LAYOUT_CUSTOM, "easyrows", parentForm);
    attrKeys = attributes;
    attributeTranslator = null;
    operatorKeys = OperatorManager.getRegisteredOperatorKeys(LogModule.getOperatorKeys());
    this.init();
  }

  /**
   * Call this to cleanup everything
   */
  public void cleanup() {
    if (columnAttribute != null) {
      while (columnAttribute.size() > 0) {
        removeRowAt(0);
      }
    }
    isinit = false;
  }

  /**
   * Call this to initialize the form
   */
  public void init() {
    if (!isinit) {
      initForm(flc, this, null);
      isinit = true;
    }
  }

  /**
   * @see org.olat.core.gui.components.form.flexible.FormBasicController#doDispose()
   */
  @Override
  protected void doDispose() {
    // help GC
    columnAttribute = null;
    columnOperator = null;
    columnValueText = null;
    columnValueSelection = null;
    columnAddRow = null;
    columnRemoveRow = null;
  }

  /**
   * @see org.olat.core.gui.components.form.flexible.FormDefaultController#formInnerEvent(org.olat.core.gui.components.form.flexible.FormItem,
   *      org.olat.core.gui.components.form.flexible.FormEvent)
   */
  @Override
  @SuppressWarnings("unused")
  protected void formInnerEvent(UserRequest ureq, FormItem source, FormEvent event) {
    if (isinit) {
      String compName = source.getName();
      if (columnAddRow.contains(compName)) {
        // add link clicked
        int clickPos = ((Integer) source.getUserObject()).intValue();
        addRowAt(clickPos + 1);
      } else if (columnRemoveRow.contains(compName)) {
        // remove link clicked
        int clickPos = ((Integer) source.getUserObject()).intValue();
        removeRowAt(clickPos);
      }
      if (compName.startsWith(PRE_ATTRIBUTE)) {
        // one of the attribute selection drop boxes has been clicked
        SingleSelection s1 = (SingleSelection) source;
        String attr;
        if (s1.isOneSelected()) {
          attr = s1.getSelectedKey();
        } else {
          // Special case: two new rows, modify the attribute on the second one
          // without touching the first one -> nothing selected on the first row
          // In this case we use the first one which is the visible one.
          attr = s1.getKey(0);
        }
        // update the value form element depending on the selected attribute
        int clickPos = ((Integer) s1.getUserObject()).intValue();
        updateValueElementForAttribute(attr, clickPos, null);
      }
    }
    // update whole container to reflect changes.
    this.flc.setDirty(true);
  }

  @Override
  protected void event(UserRequest ureq, Controller source, Event event) {
    if (event instanceof FormEvent) {
      FormEvent fe = (FormEvent) event;
      FormItem sourceItem = fe.getFormItemSource();
      String compName = sourceItem.getName();
      if (columnAddRow.contains(compName)) {
        // add link clicked
        int clickPos = ((Integer) sourceItem.getUserObject()).intValue();
        addRowAt(clickPos + 1);
      } else if (columnRemoveRow.contains(compName)) {
        // remove link clicked
        int clickPos = ((Integer) sourceItem.getUserObject()).intValue();
        removeRowAt(clickPos);
      }
    }
  }

  /**
   * @see org.olat.core.gui.components.form.flexible.FormBasicController#formOK(org.olat.core.gui.UserRequest)
   */
  @Override
  protected void formOK(UserRequest ureq) {
  // nothing to do
  }

  /**
   * @see org.olat.core.gui.components.form.flexible.FormBasicController#initFormElements(org.olat.core.gui.components.form.flexible.api.FormItemContainer,
   *      org.olat.core.gui.control.Controller, org.olat.core.gui.UserRequest)
   */
  @Override
  @SuppressWarnings( { "unused", "unchecked" })
  protected void initForm(FormItemContainer formLayout, Controller listener, UserRequest ureq) {
    //
    columnAttribute = new ArrayList();
    columnOperator = new ArrayList();
    columnValueText = new ArrayList();
    columnValueSelection = new ArrayList();
    columnAddRow = new ArrayList();
    columnRemoveRow = new ArrayList();
    // add a 0 row by default
    addRowAt(0);
    //
    ((FormLayoutContainer) formLayout).contextPut("columnAttribute", columnAttribute);
    ((FormLayoutContainer) formLayout).contextPut("columnOperator", columnOperator);
    ((FormLayoutContainer) formLayout).contextPut("columnValueText", columnValueText);
    ((FormLayoutContainer) formLayout).contextPut("columnValueSelection", columnValueSelection);
    ((FormLayoutContainer) formLayout).contextPut("columnAddRow", columnAddRow);
    ((FormLayoutContainer) formLayout).contextPut("columnRemoveRow", columnRemoveRow);
    //
  }

  /**
   * Internal helper to get the current row count
   * @return
   */
  private int getRowCount() {
    if (columnAttribute != null) {
      return columnAttribute.size();
    } else {
      return 0;
    }
  }

  /**
   * Method to get a list of extended conditions represented in this form
   *
   * @return
   */
  public List<ExtendedCondition> getAttributeConditions() {
    List<ExtendedCondition> le = new ArrayList<ExtendedCondition>();
    for (Iterator<String> iterator = columnAttribute.iterator(); iterator.hasNext();) {
      String aname = iterator.next();
      String row = aname.replace(PRE_ATTRIBUTE, "");
      SingleSelection attribute = (SingleSelection) flc.getFormComponent(PRE_ATTRIBUTE + row);
      String condName = attribute.getSelectedKey();
      SingleSelection operator = (SingleSelection) flc.getFormComponent(PRE_OPERATOR + row);
      String condOperator = operator.getSelectedKey();
      String condValue = "";
      SingleSelection valuessi = (SingleSelection) flc.getFormComponent(PRE_VALUE_SELECTION + row);
      if (valuessi.isVisible()) {
        if (valuessi.isOneSelected()) {
          condValue = valuessi.getSelectedKey();
        } else {
          // user did not actively select one, maybe because the first one was already the one he wanted. Use this one
          condValue = valuessi.getKey(0);
        }
      } else {
        TextElement valuetei = (TextElement) flc.getFormComponent(PRE_VALUE_TEXT + row);
        condValue = valuetei.getValue();
      }
      le.add(new ExtendedCondition(condName, condOperator, condValue));
    }
    return le;
  }

  /**
   * Method to initialize this form with the given extended conditions
   * @param cond
   */
  public void setAttributeConditions(List<ExtendedCondition> cond) {
    if (getRowCount() > 1) { throw new AssertException("more than one row found, don't know what do do"); }
    if (!isinit) { throw new AssertException("must call init() before calling setAttributeConditions() !"); }
    // use default initialized rows when no conditions have to be set
    if (cond.size() == 0) return;
    // remove default row from init process to make process of generating the
    // existing configuration easier
    removeRowAt(0);
    for (Iterator iterator = cond.iterator(); iterator.hasNext();) {
      ExtendedCondition extendedCondition = (ExtendedCondition) iterator.next();
      int row = getRowCount();
      // now count is always one more than the row position, thus the next position to add a row
      // is the same as the current row count
      addRowAt(row);
      try {
        // set value in attribute selection
        SingleSelection ssi = (SingleSelection) flc.getFormComponent((String) columnAttribute.get(row));
        ssi.select(extendedCondition.getAttribute(), true);
        // set value in operator selection
        ssi = (SingleSelection) flc.getFormComponent((String) columnOperator.get(row));
        ssi.select(extendedCondition.getOperator().getOperatorKey(), true);
        // set the selectable values for this attribute if available and set the
        // preselected / predefined value.
        String attribute = extendedCondition.getAttribute();
        updateValueElementForAttribute(attribute, row, extendedCondition.getValue());
      } catch (Exception e) {
        this.showError("form.easy.error.setattribute.failed");
        logError("setAttributeConditions faild", e);
      }

    }
  }

  /**
   * Internal method to add a new row at the given position
   * @param i
   */
  private void addRowAt(final int rowPos) {
    // 1) Make room for the new row if the row is inserted between existing
    // rows. Increment the row id in the user object of the form elements and
    // move them in the form element arrays
    Map formComponents = flc.getFormComponents();
    for (int move = rowPos + 1; move <= columnAttribute.size(); move++) {
      FormItem oldPos = (FormItem) formComponents.get((String) columnAttribute.get(move - 1));
      oldPos.setUserObject(Integer.valueOf(move));
      oldPos = (FormItem) formComponents.get((String) columnOperator.get(move - 1));
      oldPos.setUserObject(Integer.valueOf(move));
      oldPos = (FormItem) formComponents.get((String) columnValueText.get(move - 1));
      oldPos.setUserObject(Integer.valueOf(move));
      oldPos = (FormItem) formComponents.get((String) columnValueSelection.get(move - 1));
      oldPos.setUserObject(Integer.valueOf(move));
      oldPos = (FormItem) formComponents.get((String) columnAddRow.get(move - 1));
      oldPos.setUserObject(Integer.valueOf(move));
      oldPos = (FormItem) formComponents.get((String) columnRemoveRow.get(move - 1));
      oldPos.setUserObject(Integer.valueOf(move));
    }
    // 2) create the new row

    // get gui translated shib attributes - fallback is to use the key also as value
    final String[] guiTranslatedAttKeys = new String[attrKeys.length];
    for (int j = 0; j < attrKeys.length; j++) {
      String key = attrKeys[j];
      // OLAT-5089: use translate(String key, String[] args, boolean fallBackToDefaultLocale) version
      //            of Translator because that's the only one not
      String translated = getTranslator().translate(key, null, Level.OFF);
      if (translated.indexOf(PackageTranslator.NO_TRANSLATION_ERROR_PREFIX) == 0) {
         Translator translator = UserManager.getInstance().getPropertyHandlerTranslator(getTranslator());
         String prefix = "form.name.";
          // OLAT-5089: use translate(String key, String[] args, boolean fallBackToDefaultLocale) version
          //            of Translator because that's the only one not
         translated = translator.translate(prefix + key, null, Level.OFF);
         if (translated.indexOf(PackageTranslator.NO_TRANSLATION_ERROR_PREFIX) == 0) {
           // could not translate this key, use key for non-translated values
           guiTranslatedAttKeys[j] = key;
         } else {
           guiTranslatedAttKeys[j] = translated;
         }
      } else {
        guiTranslatedAttKeys[j] = translated;
      }
    }
    // sort after the values
    ArrayHelper.sort(attrKeys, guiTranslatedAttKeys, false, true, true);
    // use this sorted keys-values
    SingleSelection attribute = uifactory.addDropdownSingleselect(PRE_ATTRIBUTE + rowCreationCounter, null, flc, attrKeys, guiTranslatedAttKeys, null);
    attribute.setUserObject(Integer.valueOf(rowPos));
    attribute.addActionListener(this, FormEvent.ONCHANGE);
    columnAttribute.add(rowPos, attribute.getName());

    // 2b) Operator selector
    String[] values = OperatorManager.getRegisteredAndAlreadyTranslatedOperatorLabels(getLocale(), operatorKeys);
    FormItem operator = uifactory.addDropdownSingleselect(PRE_OPERATOR + rowCreationCounter, null, flc, operatorKeys, values, null);
    operator.setUserObject(Integer.valueOf(rowPos));
    columnOperator.add(rowPos, operator.getName());

    // 2c) Attribute value - can be either a text input field or a selection
    // drop down box - create both and hide the selection box
    //
    TextElement valuetxt = uifactory.addTextElement(PRE_VALUE_TEXT + rowCreationCounter, null, -1, null, flc);
    valuetxt.setDisplaySize(25);
    valuetxt.setNotEmptyCheck("form.easy.error.attribute");
    valuetxt.setUserObject(Integer.valueOf(rowPos));
    columnValueText.add(rowPos, valuetxt.getName());
    // now the selection box
    FormItem iselect = uifactory.addDropdownSingleselect(PRE_VALUE_SELECTION + rowCreationCounter, null, flc, new String[0], new String[0], null);
    iselect.setUserObject(Integer.valueOf(rowPos));
    iselect.setVisible(false);
    columnValueSelection.add(rowPos, iselect.getName());
    // 3) Init values for this row, assume selection of attribute at position 0
    updateValueElementForAttribute(attribute.getKey(0), rowPos, null);

    // 4) Add the 'add' and 'remove' buttons
    FormLinkImpl addL = new FormLinkImpl("add_" + rowCreationCounter, "add." + rowPos, "+", Link.BUTTON_SMALL + Link.NONTRANSLATED);
    addL.setUserObject(Integer.valueOf(rowPos));
    flc.add(addL);
    columnAddRow.add(rowPos, addL.getName());
    //
    FormLinkImpl removeL = new FormLinkImpl("remove_" + rowCreationCounter, "remove." + rowPos, "-", Link.BUTTON_SMALL + Link.NONTRANSLATED);
    removeL.setUserObject(Integer.valueOf(rowPos));
    flc.add(removeL);
    columnRemoveRow.add(rowPos, removeL.getName());

    // new row created, increment counter for unique form element id's
    rowCreationCounter++;
  }

  /**
   * Internal method to remove the row at the given position.
   * @param clickPos The row to be removed
   */
  private void removeRowAt(int clickPos) {
    // 1) remove the form elements from the form container
    flc.remove(flc.getFormComponent((String) columnAttribute.get(clickPos)));
    columnAttribute.remove(clickPos);
    flc.remove(flc.getFormComponent((String) columnOperator.get(clickPos)));
    columnOperator.remove(clickPos);
    flc.remove(flc.getFormComponent((String) columnValueText.get(clickPos)));
    columnValueText.remove(clickPos);
    flc.remove(flc.getFormComponent((String) columnValueSelection.get(clickPos)));
    columnValueSelection.remove(clickPos);
    flc.remove(flc.getFormComponent((String) columnAddRow.get(clickPos)));
    columnAddRow.remove(clickPos);
    flc.remove(flc.getFormComponent((String) columnRemoveRow.get(clickPos)));
    columnRemoveRow.remove(clickPos);
    // 2) adjust all rows below the removed row. set the new row id in the user
    // object of the form element and move the element in the element arrays
    for (int move = clickPos; move < columnAttribute.size(); move++) {
      FormItem oldPos = flc.getFormComponent((String) columnAttribute.get(move));
      oldPos.setUserObject(Integer.valueOf(move));
      oldPos = flc.getFormComponent((String) columnOperator.get(move));
      oldPos.setUserObject(Integer.valueOf(move));
      oldPos = flc.getFormComponent((String) columnValueText.get(move));
      oldPos.setUserObject(Integer.valueOf(move));
      oldPos = flc.getFormComponent((String) columnValueSelection.get(move));
      oldPos.setUserObject(Integer.valueOf(move));
      oldPos = flc.getFormComponent((String) columnAddRow.get(move));
      oldPos.setUserObject(Integer.valueOf(move));
      oldPos = flc.getFormComponent((String) columnRemoveRow.get(move));
      oldPos.setUserObject(Integer.valueOf(move));
    }
  }

  /**
   * Internal method to update a row's value element. This can be a text input
   * box or a selection drop down depending on the shibboleth module
   * configuration and the selected attribute. The method will set the given
   * value as the users selected / inputed value
   *
   * @param attribute The attribute key. Must not be NULL
   * @param row the row ID
   * @param value The value that should be selected / used in the text input field. Can be NULL.
   */
  private void updateValueElementForAttribute(final String attribute, final int row, String value) {
    // set value for this field: the selection box or the text element
    final String[] selectableKeys = (getAttributeTranslator() != null) ?
        getAttributeTranslator().getSelectableValuesForAttribute(attribute) :
          null;
    // Get the value text input and selection drop down form elements for this
    // row. Don't use the element name since there we have the global row
    // counter in the name and _not_ the current row id!
    SingleSelection iselect = (SingleSelection) flc.getFormComponent(columnValueSelection.get(row));
    TextElement tei = (TextElement) flc.getFormComponent(columnValueText.get(row));

    if (selectableKeys != null) {
      // to set value on selection drop down we first need to remove it and create it as new one with new keys
      // create an object array with key - value pairs in it
      String[] guiTranslatedKeys = new String[selectableKeys.length];
      for (int i = 0; i < selectableKeys.length; i++) {
        String key = selectableKeys[i];
        String translated = getTranslator().translate(attribute + "." + key);
        if (translated.indexOf(PackageTranslator.NO_TRANSLATION_ERROR_PREFIX) == 0) {
          // could not translate this key, use key for non-translated values
          guiTranslatedKeys[i] = key;
        } else {
          guiTranslatedKeys[i] = translated;
        }
      }     
      // sort the key-value-pairs by value
      ArrayHelper.sort(selectableKeys, guiTranslatedKeys, false, true, true);
     
      // update keys and values now
      iselect.setKeysAndValues(selectableKeys, guiTranslatedKeys, null);
      // set user value
      if (value != null) {
        // check if stored value exists, otherwise don't select anything
        if (Arrays.asList(selectableKeys).contains(value)) {
          iselect.select(value, true);
        } else {
          // ups, maybe this value has been removed from the list? maybe a programming error?
          logWarn("could not select value::" + value + " for shibboleth attribute::" + attribute
              + " in course easy mode, not found in selectable list", null);
        }
      }
      iselect.setVisible(true);
      // set alternative text input field as non visible
      tei.setVisible(false);
    } else {
      // update text element visibility and the value
      tei.setValue(value);
      tei.setVisible(true);
      // and hide selection box
      iselect.setVisible(false);
    }
  }

  private AttributeTranslator getAttributeTranslator() {
    return attributeTranslator;
  }

  /**
   * Internal helper to create a sting array that contains all shibboleth
   * attributes that can be selected in the drop down
   *
   * @return String[] - will never returh null
   */
  private String[] getShibAttributes() {
    if (ShibbolethModule.isEnableShibbolethLogins()) {
      AttributeTranslator attTrans = getAttributeTranslator();
      Set<String> attributes = attTrans.getTranslateableAttributes();
      String[] outNames = new String[attributes.size()];
      int i = 0;
      for (String attribute : attributes) {
        outNames[i] = attTrans.translateAttribute(attribute);
        i++;
      }
      return outNames;
    }
    return new String[0];
  }

  /**
   * Checks if this form produces an error
   * @return true: has an error; false: is valid
   */
  public boolean hasError() {
    for (Iterator<String> iterator = columnValueText.iterator(); iterator.hasNext();) {
      String name = iterator.next();
      TextElement tei = (TextElement) flc.getFormComponent(name);
      if (tei.isVisible() && tei.getValue().trim().length() == 0) { return true; }
    }
    return false;
  }

  /**
   * Get the form item that forms this subform
   * @return
   */
  public FormItem getFormItem() {
    return this.flc;
  }
 
}
TOP

Related Classes of org.olat.course.condition.AttributeEasyRowAdderController

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.