<?
//******************************************************************************
//******************               PLUGIN                   ********************
//******************************************************************************
/**
 * FUNCTION getPlugin()
 * Get plugin by id from database. If no id is specified, get an array with all plugins
 *
 * @param integer $id: Plugin Id or "" for all plugins
 * @return array $ret tns:PluginArray with one / all plugins installed on the system or "".
 * @see putPlugin
 * @see delPlugin
 * @see getPluginByName
 * @see getPluginByPackageName
 * @see getPluginByEntity
 * @see getPluginEntities
 */
function getPlugin($id){
  global $db;
  $ret=array();
  if(($id!=0)&&($id!='')) $condition=" WHERE id='".string2sql($id)."'";
  $qry="SELECT * FROM cw_plugin $condition";
  $res=$db->Execute($qry);
  if($res!==false){
    while($arr = $res->FetchRow()){
      foreach($arr as $field=>$value){
        $v[$field]=$value;
      }
      $ret[]=$v;
    }
  }
  return $ret;
}
/**
 * FUNCTION getPluginByName()
 * Get plugin by name from database.
 *
 * @param string $name: Plugin short name i.e "Wake-Up_Call"
 * @return array $ret tns:PluginArray with one plugin or "" if plugin is not installed
 * @see getPlugin
 * @see getPluginByPackageName
 * @see getPluginByEntity
 * @see putPlugin
 * @see getPluginEntities
 */
function getPluginByName($name){
  global $db;
  $ret=array();
  $qry="SELECT * FROM cw_plugin WHERE name='".string2sql($name)."'";
  $res=$db->Execute($qry);
  while($arr=$res->FetchRow()){
    foreach($arr as $field=>$value){
      $v[$field]=$value;
    }
    $ret[]=$v;
  }
  return $ret;
}
/**
 * FUNCTION getPluginByPackageName()
 * Get plugin by packageName (=name.version) from database.
 *
 * @param string $packageName i.e "Wake-Up_Call.1.10.0"
 * @return array $ret tns:PluginArray with one plugin or "" if plugin is not installed
 * @see getPlugin
 * @see getPluginByName
 * @see getPluginByEntity
 * @see putPlugin
 * @see delPlugin
 * @see getPluginEntities
 */
function getPluginByPackageName($packageName){
  global $db;
  $ret=array();
  $qry="SELECT * FROM cw_plugin WHERE package_name='".string2sql($packageName)."'";
  $res=$db->Execute($qry);
  while($arr=$res->FetchRow()){
    foreach($arr as $field=>$value){
      $v[$field]=$value;
    }
    $ret[]=$v;
  }
  return $ret;
}
/**
 * FUNCTION getPluginEntities()
 * Get plugin entities for an activated plugin from database. If no id is specified, get array with entities for all plugins
 *
 * @param integer $id: PluginId or "" for all plugins
 * @return array $ret tns:PluginEntityArray or "" if plugin is not installed
 * @see getPlugin
 * @see getPluginByName
 * @see getPluginByPackageName
 * @see getPluginByEntity
 * @see putPlugin
 * @see delPlugin
 */
function getPluginEntities($id){
  global $db;
  $ret=array();
  if(($id!=0)&&($id!='')) $condition=" WHERE plugin_id='".string2sql($id)."'";
  $qry="SELECT * FROM cw_plugin_entity $condition";
  $res=$db->Execute($qry);
  while($arr = $res->FetchRow()){
    foreach($arr as $field=>$value){
      $v[$field] = $value;
    }
    $ret[] = $v;
  }
  return $ret;
}
/**
 * FUNCTION getPluginbyEntity()
 * Get plugin by vo-object (entity). If more than one plugin uses that entity, a list of plugins is returned.
 *
 * @param string $type: Entity type as string like "application", "macro" or "queue". String because entities do not have unique id's
 * @param integer $id: Entity Id
 * @return array $ret tns:PluginArray with plugins that uses the specified entity or ""
 * @see getPlugin
 * @see getPluginByName
 * @see getPluginByPackageName
 * @see putPlugin
 * @see delPlugin
 */
function getPluginbyEntity($eType, $eId){
  global $db;
  $ret = array();
  // Use new Syntax that is ANSI-92 conform
  $qry="SELECT * ";
  $qry.="FROM cw_plugin as p ";
  $qry.="INNER JOIN cw_plugin_entity AS e ON p.id = e.plugin_id ";
  $qry.="WHERE e.entity_type = '".string2sql($eType)."' ";
  $qry.="AND e.entity_id = '".string2sql($eId)."' ";
  $res = $db->Execute($qry);
  while($arr = $res->FetchRow()){
    foreach($arr as $field=>$value){
      $v[$field] = $value;
    }
    $ret[] = $v;
  }
  return $ret;

}
/**
 * FUNCTION putPlugin()
 * Insert plugin from XML String to the system. Putplugin works in the following steps:
 *  - validate xml parameter
 *  - read plugin data, put vo-objects in database etc.
 *  - fix references based on id's between vo-objects. I.e. app uses macro with id=3
 *  - update database with plugin info
 *
 * Fixes automatically references based on id's between vo-objects like application->macro
 * Note: So far, the plugin support is limited to macros, applications and queues
 * @param string $pli XML of plugin information
 * @param string $pld XML of plugin data (plugin objectcs)
 * @return integer 1=ok 0 or less failure
 * @see delPlugin
 * @see getPluginByName
 * @see getPluginByPackageName
 * @todo handle errors, updates and return errorcode. Support more objects.
 */
function putPlugin($pli, $pld){
  global $db;
  /**
   * Get plugin information
   */
  $xmlArr=xml2array($pli);
  //errArr($xmlArr);
  //to be valid, root node must be <plugininfo>. otherwise exit
  if($xmlArr[0]['name'] != 'plugininfo'){
    //error_log("missing valid plugininfo. no valid plugin");
    return 0;
  }
  //Read plugin info into array.
  foreach ($xmlArr[0]["elements"] as $el){
    $desc[$el["name"]]=$el["text"];
  }
  //Plugin with the same name must not be activated. otherwise exit
  $ret=getPluginByName($desc['name']);
  if(count($ret)){
   //error_log("plugin is with the same name is already activated");
   return 0;
  }
  /**
   * Get plugin data
   */
  $xmlArr=xml2array($pld);
  //errArr($xmlArr);
  // root node must be <plugindata> to be valid
  if($xmlArr[0]['name'] != 'plugindata'){
    //error_log("missing valid plugindata. no valid plugin");
    return 0;
  }
  /**
   * Activate plugin
   *
   * ok - seems valid. lets walk though the vo-objects and add them to the system
   * however, we need to fix references, because all new objects will get new id's
   * i.e if a plugin imports an app and a macro, then we need to adjust the reference to the macro in the app.
   * for this we use an associative array that maps old and new id's.
   */
  // map old and new id's. [type][$importId]=newId
  $idMapper=array();
  // need to loop 2 times.
  // 1st to get the new id's for all elements if necessary
  // 2nd to fix the references via $idMapper and rebuild the objects
  // if all objects already existed, we break and omnit the 2nd loop.
  $lStates = array('stInit', 'stFixId');
  foreach ($lStates as $lState) {
    foreach ($xmlArr[0]['elements'] as $item){
      unset($entry);
      $type = $item["name"];
      switch($type){
        // xml element is macro
        // we do not need to fix anything in stFixId
        case "macro":
          if($lState == 'stFixId'){ break;}
          foreach ($item["elements"] as $el) {
            $entry[$el["name"]] = $el["text"];
            // get the $importId for the idMappper
            if ($el["name"] == "id") { $importId = $el["text"]; }
            // get the parameter section
            if ($el["name"] == "parameter") {
              $txt = "";
              // Not every plugin or macro has paramter its optional
              if (is_array($el["elements"])) {
                foreach ($el["elements"] as $e) {
                  $txt .= "<param name=\"" . $e["attributes"]["name"] . "\" type=\"" . $e["attributes"]["type"];
                  $txt .= "\" label=\"" . $e["attributes"]["label"] . "\" description=\"" . $e["attributes"]["description"] . "\" />";
                }
              }
              $entry[$el["name"]] = $txt;
            } //if  parameter
          }
          // put macro by name, not by id.
          $newId = putMacro($entry, false);
          if ($lState == 'stInit') {
            $entity[$type][] = $newId;
            //error_log("inserted macro id $newId");
            // add the ID to the mapping table if it is different
            if ($importId != $newId) { $idMapper[$type][$importId] = $newId; }
          }
        break;
        // xml element is application
        // we need to fix the id's of macro references in stFixId
        case "application":
          foreach ($item["elements"] as $el) {
            // if tag groups then enter the group list
            if ($el["name"]=="groups") {
              foreach($el["elements"] as $group) $groups[$group["name"]]=$group["text"];
              $entry[$el["name"]] = $groups;
            }
            else $entry[$el["name"]] = $el["text"];
            // check the id
            if ($el["name"] == "id") {
              // in stInit store the $importId for the idMapper
              if ($lState == 'stInit') { $importId = $el["text"]; }
              // fixid: in stFixId use the newId assigned as in stInit
              if ($lState == 'stFixId') {
                if ($idMapper['application'][$el["text"]] != "") { $entry[$el["name"]] = $idMapper['application'][$el['text']]; }
              }
            }
            // fix reference to the macro
            if ($el["name"] == "macro") {
              if  ($lState == 'stFixId') {
                if ($idMapper['macro'][$el["text"]] != "") { $entry[$el["name"]] = $idMapper['macro'][$el['text']]; }
              }
            }
          } // foreach
          // put application by name, not by id. request reload
          $newId = putApplication($entry, false);
          if ($lState == 'stInit') {
           $entity[$type][] = $newId;
            //error_log("inserted application id $newId");
            // add the ID to the mapping table if it is different
            if ($importId != $newId) { $idMapper[$type][$importId] = $newId; }
          }
        break;
        /*
        // xml element is queue
        // so far we do not support extensions and therefore we do not need to fix anything in stFixId
        // even we would, how would we translate queuemember?
        case "queue":
          // if we suport extensions, we might have to change this!
          if ($lState == 'stFixId') {  break; }

          foreach ($item["elements"] as $el) {
            $entry[$el["name"]] = $el["text"];
            // get the $importId
            if ($el["name"] == "id") { $importId = $el["text"]; }
            if ($el["name"] == "member") {
              foreach ($el["elements"] as $e) { $v[] = $e["text"]; }
              $entry['member'] = $v;
            }
          }
          // putQueue by name, not by id
          $newId = putQueue($entry, false);
          if ($lState == 'stInit') {
            $entity[$type][] = $newId;
            //error_log("inserted queue id $newId");
            // add the ID to the mapping table if it is different
            if ($importId != $newId) { $idMapper[$type][$importId] = $newId; }
          }
        break;
        */
        default:
          //error_log("item not supported");
        break;
      }
    } // foreach tag
    // if all objects have existed, than no id's will have changed. so we do not need a 2nd run
    if ((count($idMapper) == 0) and ($lState == 'stInit')) { break; }
  } // foreach
  /**
   * Update database with the new plugin information
   */
  // build information array. map database fields
  $d_name = $desc['name'];
  $d_label = $desc['label'];
  $d_package_name = $desc['packageName'];
  $d_version = $desc['version'];
  $d_date = $desc['date'];
  $d_description = $desc['description'];
  $d_author_name = $desc['authorName'];
  $d_author_email = $desc['authorEmail'];
  $d_author_website = $desc['authorWebsite'];
  $d_vo_version = $desc['voVersion'];
  $d_vo_pl_version = $desc['voPlVersion'];
  // update db
  $qry = "INSERT INTO cw_plugin ";
  $qry .= " (name, label, package_name, version, date, description, author_name, author_email, author_website, vo_version, vo_pl_version) ";
  $qry .= " VALUES ";
  $qry .= " ('".string2sql($d_name)."','".string2sql($d_label)."','".string2sql($d_package_name)."','".string2sql($d_version)."',";
  $qry .= "'$d_date','".string2sql($d_description)."','".string2sql($d_author_name)."','".string2sql($d_author_email)."',";
  $qry .= "'".string2sql($d_author_website)."','".string2sql($d_vo_version)."','".string2sql($d_vo_pl_version)."')";
  //error_log($qry);
  $res=$db->Execute($qry);
  // get new plugin id
  $plugin_id = $db->Insert_ID();
  // update cw_plugin_entity
  foreach ($entity as $etype=>$elist) {
    foreach ($elist as $elId) {
    $d_plugin_id = $plugin_id;
    $d_entity_id = $elId;
    // we store the entity type verbose like "macro" or "application".
    // this is because we do not have unique id's for all entity types.
    // might change in future version
    $d_entity_type = $etype;
    // not supported so far because entities do not maintain a version
    $d_entity_version = "";
    $qry = "INSERT INTO cw_plugin_entity ";
    $qry .= " (plugin_id, entity_type, entity_id, entity_version) ";
    $qry .= " VALUES ";
    $qry .= " ($d_plugin_id,'".string2sql($d_entity_type)."',$d_entity_id,'".string2sql($d_entity_version)."') ";
    $res=$db->Execute($qry);
    }
  }
  //return
  return 1;
}
/**
 * FUNCTION putPlugin()
 * Delete a plugin from the system by $id. Removes all entities (vo-objects) that are part of the plugin in case they are not used by other plugins
 *
 * @param integer $id of plugin to delete
 * @return integer 1
 * @see putPlugin
 * @see getPluginByName
 * @see getPluginByPackageName
 * @see getPluginByEntity
 * @see getPluginEntities
 * @todo handle errors, return errorcodes, pass info about vo-objects back  (removed/kept)
 */
function delPlugin($id) {
  global $db;
  // remove vo-objects (entities) in case they are not used by other plugins
  $entities=getPluginEntities($id);
  foreach($entities as $entity){
    $type = $entity['entity_type'];
    $plList = getPluginbyEntity($entity['entity_type'], $entity['entity_id']);
    if(is_array($plList) and (count($plList) == 1)){
      // entity is only used by one plugin, so we remove it
      switch ($type) {
        case "application":
          $ret = delApplication($entity['entity_id'], true);
        break;
        case "macro":
          $ret = delMacro($entity['entity_id'], true);
        break;
        /*
        case "queue":
          $ret = delQueue($entity['entity_id']);
        break;
        */
      } // switch
    } // if
  } // foreach
  // delete plugin describtion
  $qry="DELETE FROM cw_plugin WHERE id='".string2sql($id)."' ";
  $ret = $db->Execute($qry);
  // delete plugin objects
  $qry="DELETE FROM cw_plugin_entity WHERE plugin_id='".string2sql($id)."' ";
  $ret = $db->Execute($qry);
  //return
  return 1;
}
?>