Wikifing help links

From Achievo/ATK Wiki

Jump to: navigation, search

I'm a firm believer in user created / contributed help. In that spirit I wanted to provide context level help to my ATK application that was wiki driven. This method hijacks the already built in help links system and redirects them away from the language / help files and instead directs them to appropriate wiki pages. This method is smart enough to use the language files for new node and attribute names so that users should have a fairly seamless experience.

These changes were made to ATK 6.2. Different versions may require slight modifications.

For a working example check out MySHI next release 0.03b or greater.

Some developers may prefer Editing files with the atkFileEditor (edit help files) but that doesn't give revision history and other wiki niceties.

Contents

Concerns

The biggest problem is that this method makes changes to some of the base ATK classes and so special work will be necessary every time ATK is upgraded. Unfortunately I couldn't get the atkClassLoader() to work. Perhaps someone else can come up with a more friendly implementation method.

This method will use the language files to create page names so a developer will want be aware of page names when changing the language files so that pages don't become disconnected and alienated.

This may not handle internationalization as well as possible. The language files are used to determine the wiki page name. However, I'm fairly certain that at least MediaWiki has international support and it might be worth looking into how to integrate better with that.

If an app is using a wiki that documents more than just the application it might be helpful to use categories such as MediaWiki uses.

config.inc.php

Add the following to file.

//Base URL of help site.
$config_help_site = "http://somesite.net/mediawiki/index.php?title=";

atk/class.atknode.inc

Change the original ATK 6.2 helpURL method

    /**
     * Get the help url for this node.
     *
     * Retrieves the url of the help popup, if there is help available for
     * this node.
     * @return String The help url, or an empty string if help is not
     *                available.
     */
    function helpUrl()
    {
      $language = atkconfig("language");
      $node = $this->m_type;

      $file   = moduleDir($this->m_module)."help/".$language."/help.".$node.".inc";
      $helpmodule = "";
      if (file_exists($file))
      {
        $helpmodule = $this->m_module;
      }
      else
      {
        // bwc
        $file   = "help/".$language."/help.".$node.".inc";
        if (!file_exists($file))
        {
          // no help available..
          return "";
        }
      }

      $name = atktext("help");
      return atkPopup('atk/popups/help.inc','node='.$node.($helpmodule!=""?"&module=".$helpmodule:""),$name,650,650,'yes','no');
    }

To

    /**
     * Get the help url for this node.
     *
     * Retrieves the url of the help popup, if there is help available for
     * this node.
     * @return String The help url, or an empty string if help is not
     *                available.
     */
    function helpUrl()
    {
      $module = $this->getModule();
      if (!$module) $module = "atk";
      
      $nodetxt = atktext($this->m_type, $module, "", "", "", true);
      if (!$nodetxt) {
        $nodetxt = $this->m_type;
      }
      
      $tooltip .= atkconfig("help_site");
      $tooltip .= $nodetxt;
      $tooltip .= '" target="help"';
      
      return $tooltip;

    }

atk/attributes/class.atkattribute.inc

Change the original ATK 6.2 getToolTip method

    /**
     * Retrieve the html/javascript code for showing the tooltip for this attribute.
     *
     * @return String HTML
     */
    function getToolTip()
    {
      $module = $this->getModule();
      if (!$module) $module = "atk";

      $node = &$this->m_ownerInstance;
      $ttip = atktext($node->m_type."_".$this->fieldName()."_tooltip", $module, "", "", "", true);

      $tooltip = '';

      if ($ttip)
      {
        $theme = &atkinstance("atk.ui.atktheme");
        $tipimg = $theme->imgPath("help.gif");

        $onelinetip = preg_replace('/([\r\n])/e',"",$ttip);
        $tooltip = '<img align="top" src="'.$tipimg.'" border="0" alt="'.$onelinetip.'" onClick="javascript:alert(\''.str_replace("\n", '\n', addslashes($ttip)).
        '\')" onMouseOver="javascript:window.status=\''. addslashes($onelinetip).'\';">';
      }
      return $tooltip;
    }

To

    /**
     * Retrieve the html/javascript code for showing the tooltip for this attribute.
     *
     * @return String HTML
     */
    function getToolTip()
    {

      $module = $this->getModule();
      if (!$module) $module = "atk";

      $node = &$this->m_ownerInstance;

      $nodetxt = atktext($node->m_type, $module, "", "", "", true);
      if (!$nodetxt) {
        $nodetxt = $node->m_type;
      }
      
      $attributetxt = atktext($this->fieldName(), $module, "", "", "", true);      
      if (!$attributetxt) {
        $attributetxt = $this->fieldName();
      }
      
      $link = $nodetxt . " - " . $attributetxt;
      
      $tooltip = ' ';
      $tooltip .= '<a href="';
      $tooltip .= atkconfig("help_site");
      $tooltip .= $link;
      $tooltip .= '" target="help">?</a>';

      return $tooltip;
      
    }

atk/attributes/class.atktabbedpane.inc

Change the original ATK 6.2 edit and display methods

    function edit($defaults="", $fieldprefix="", $mode="")
    {
      $node = &$this->m_ownerInstance;
      $arr = array("hide"=>array());
      //get data
      $data = $this->_addToEditArray($mode, $arr, $defaults, $defaults['atkerror'], $fieldprefix);

      // Handle errors - need more testing for move to right tab
      /*$errors = array();
      if (count($data['error']) > 0)
      {
        $error_title = '<b>'.atktext('error_formdataerror').'</b>';

        foreach ($data["error"] as $error)
        {
          if(in_array($error['attribname'],array_keys($this->m_attribsList)))
          {
            $error['tab'] = $this->m_attribsList[$error['attribname']];
          }

          if ($error['err'] == "error_primarykey_exists")
          {
            $pk_err_attrib[] = $error['attrib_name'];
          }
          else
          {
            $type = (empty($error["node"]) ? $node->m_type : $error["node"]);

            if (count($node->getTabs($node->m_action)) > 1 && $error["tab"])
              $error_tab = ' ('.atktext("error_tab").' '.'<a href="javascript:showTab(\''.$error["tab"].'\');">'.atktext(array("tab_".$error["tab"], $error["tab"]),$node->m_module, $node->m_type).'</a> )';
            else $error_tab = "";

            if(!is_array($error['attrib_name']))
            {
              $label = atktext($error['attrib_name'], $node->m_module, $type);
            }
            else
            {
              $label = array();
              foreach($error['attrib_name'] as $attrib)
                $label[] = atktext($attrib, $node->m_module, $type);

              $label= implode(", ", $label);
            }

            $errors[] = array("msg"=>$error['msg'].$error_tab, "label"=>$label);
          }
        }
        if (count($pk_err_attrib)>0) // Make primary key error message
        {
          for($i=0;$i<count($pk_err_attrib); $i++)
          {
            $pk_err_msg .= atktext($pk_err_attrib[$i], $node->m_module);
            if (($i+1) < count($pk_err_attrib)) $pk_err_msg .= ", ";
          }
          $errors[] = array("label"=>atktext("error_primarykey_exists"),
                            "msg"=>$pk_err_msg);
        }
      }*/

      // Handle fields
      // load images
      $theme = &atkinstance("atk.ui.atktheme");
      $tipimg = $theme->imgPath("help.gif");
      $reqimg = '<img align="top" src="'.$theme->imgPath("required_field.gif").'" border="0"
                  alt="'.atktext("field_obligatory").'" title="'.atktext("field_obligatory").'">';

      /* display the edit fields */
      $fields = array();
      $tab = $this->getDefaultTab();

      for ($i = 0, $_i= count($data["fields"]); $i<$_i; $i++)
      {
        $field = &$data["fields"][$i];
        $tplfield = array();

		    $tplfield["tab"] =  $field["tabs"];

		    $tplfield["initial_on_tab"] = $tplfield["tab"]==$tab;

		    $tplfield["class"] = "tabbedPaneAttr tabbedPaneTab{$field['tabs']}";

		    // Check if there are attributes initially hidden on this tabbedpane
		    if ($field["attribute"]->isInitialHidden($defaults))
		        $tplfield["class"].= " atkAttrRowHidden";

		    $tplfield["rowid"] = "tabbedPaneAttr_".($field['id']!=''?$field['id']:getUniqueID("anonymousattribrows")); // The id of the containing row

		    /* check for separator */
        if ($field["html"] == "-" && $i > 0 && $data["fields"][$i-1]["html"] != "-")
        {
          $tplfield["line"] = "<hr>";
        }
        /* double separator, ignore */
        elseif ($field["html"] == "-")
        {
        }
        /* only full HTML */
        elseif (isset($field["line"]))
        {
          $tplfield["line"] = $field["line"];
        }
        /* edit field */
        else
        {
          if ($field["attribute"]->m_ownerInstance->getNumbering())
          {
            atkViewEditBase::_addNumbering($field, $tplfield, $i);
          }

          /* does the field have a label? */
          if ((isset($field["label"]) && $field["label"]!=="AF_NO_LABEL") && !$this->isAttributeSingleOnTab($field['name'])
                              || !isset($field["label"]))
          {
            if ($field["label"] == "")
            {
              $tplfield["label"] = "";
            }
            else
            {
              $tplfield["label"] = $field["label"];
              if ($field["error"]) // TODO KEES
              {
                $tplfield["error"] = $field["error"];
              }
            }
          }
          else
          {
          	$tplfield["label"]="AF_NO_LABEL";
          }

          /* obligatory indicator */
          if ($field["obligatory"])
          {
            $tplfield["label"];
            $tplfield["obligatory"] = $reqimg;
          }

          /* html source */
          $tplfield["widget"] = $field["html"];
          $editsrc = $field["html"];

          /* tooltip */
          if (is_object($node->m_attribList[$field['name']]))
          {
            $module = $node->m_attribList[$field['name']]->getModule();
          }
          if (!$module) $module = "atk";
          $ttip = atktext($node->m_type."_".$field["name"]."_tooltip", $module, "", "", "", true);

          if ($ttip)
          {
            $onelinetip = preg_replace('/([\r\n])/e',"",$ttip);
            $tooltip = '<img align="top" src="'.$tipimg.'" border="0" alt="'.$onelinetip.'" onClick="javascript:alert(\''.str_replace("\n", '\n', addslashes($ttip)).
                                    '\')" onMouseOver="javascript:window.status=\''. addslashes($onelinetip).'\';">';
            $tplfield["tooltip"] = $tooltip;
            $editsrc.=$tooltip." ";
          }

          $tplfield['id']=str_replace('.','_',$node->atknodetype().'_'.$field["id"]);

          $tplfield["full"] = $editsrc;
        }
        $fields[] = $tplfield; // make field available in numeric array
        $params[$field["name"]] = $tplfield; // make field available in associative array
      }

      $ui = &$node->getUi();
      $page = &$node->getPage();

      $result = "";

      foreach ($arr["hide"] as $hidden)
      {
        $result.= $hidden;
      }

      $params["activeTab"] = $tab;
      $params["panename"] = $this->m_name;
      $params["fields"] = $fields; // add all fields as an numeric array.
      $params["errortitle"] = $error_title;
      $params["errors"] = $errors; // Add the list of errors.
      if (!$template) $template = $node->getTemplate($mode, $record, $tab);
      $result .= $ui->render("tabbededitform.tpl", $params);

      $content = $this->tabulate($mode, $result, $fieldprefix);

      return $content;
    }

    /**
    * Display a tabbed pane with attributes
    * @param $record  Array with fields
    * @return html code
    */
    function display($record, $mode="")
    {
    	// get active tab
      $active_tab = $this->getDefaultTab();
      $fields = array();

      $node = &$this->m_ownerInstance;
      $ui = &$node->getUi();

      // For all attributes we use the display() function to display the
      // attributes current value. This may be overridden by supplying
      // an <attributename>_display function in the derived classes.
      foreach ($this->m_attribsList as $name=>$tab)
      {
        $p_attrib = &$node->getAttribute($name);
        if(is_object($p_attrib))
        {
          $tplfield = array();
          if (!$p_attrib->hasFlag(AF_HIDE_VIEW))
          {
            $fieldtab = $this->m_attribsList[$name];

            $tplfield["class"] = "tabbedPaneAttr tabbedPaneTab{$fieldtab}";
	  	      $tplfield["rowid"] = "tabbedPaneAttr_".getUniqueID("anonymousattribrows"); // The id of the containing row
            $tplfield["tab"] = $tplfield["class"]; // for backwards compatibility

            $tplfield["initial_on_tab"] = ($fieldtab==$active_tab);

            // An <attributename>_display function may be provided in a derived
            // class to display an attribute. If it exists we will use that method
            // else we will just use the attribute's display method.
            $funcname = $p_attrib->m_name."_display";
            if (method_exists($node, $funcname)) $editsrc = $node->$funcname($record, "view");
            else $editsrc=$p_attrib->display($record, "view");

            /* tooltip */
            $module = $p_attrib->getModule();
            if (!$module) $module = "atk";
            $ttip = atktext($node->m_type."_".$name."_tooltip", $module, "", "", "", true);

            if ($ttip)
            {
              $theme = &atkinstance("atk.ui.atktheme");
              $tipimg = $theme->imgPath("help.gif");

              $onelinetip = preg_replace('/([\r\n])/e',"",$ttip);
              $tooltip = '<img align="top" src="'.$tipimg.'" border="0" alt="'.$onelinetip.'" onClick="javascript:alert(\''.str_replace("\n", '\n', addslashes($ttip)).
                                      '\')" onMouseOver="javascript:window.status=\''. addslashes($onelinetip).'\';">';
              $tplfield["tooltip"] = $tooltip;
              $editsrc.=$tooltip." ";
            }

            $tplfield["full"] = $editsrc;
            $tplfield["widget"] = $editsrc; // in view mode, widget and full are equal

            // The Label of the attribute (can be suppressed with AF_NOLABEL or AF_BLANKLABEL)
            // For each attribute, a txt_<attributename> must be provided in the language files.
            if (!$p_attrib->hasFlag(AF_NOLABEL) && !$this->isAttributeSingleOnTab($name))
            {
              if ($p_attrib->hasFlag(AF_BLANKLABEL))
              {
                $tplfield["label"] = "";
              }
              else
              {
                $tplfield["label"] = $p_attrib->label($record);
              }
            }
            else
            {
              // Make the rest fill up the entire line
              $tplfield["label"] = "";
              $tplfield["line"] = $tplfield["full"];
            }
            $fields[] = $tplfield;
          }
        }
        else
        {
          atkerror("Attribute $name not found!");
        }
      }
      $innerform = $ui->render($node->getTemplate("view", $record, $tab), array("fields"=>$fields));

      return $this->tabulate("view", $innerform);
    }

To

    function edit($defaults="", $fieldprefix="", $mode="")
    {
      $node = &$this->m_ownerInstance;
      $arr = array("hide"=>array());
      //get data
      $data = $this->_addToEditArray($mode, $arr, $defaults, $defaults['atkerror'], $fieldprefix);

      // Handle errors - need more testing for move to right tab
      /*$errors = array();
      if (count($data['error']) > 0)
      {
        $error_title = '<b>'.atktext('error_formdataerror').'</b>';

        foreach ($data["error"] as $error)
        {
          if(in_array($error['attribname'],array_keys($this->m_attribsList)))
          {
            $error['tab'] = $this->m_attribsList[$error['attribname']];
          }

          if ($error['err'] == "error_primarykey_exists")
          {
            $pk_err_attrib[] = $error['attrib_name'];
          }
          else
          {
            $type = (empty($error["node"]) ? $node->m_type : $error["node"]);

            if (count($node->getTabs($node->m_action)) > 1 && $error["tab"])
              $error_tab = ' ('.atktext("error_tab").' '.'<a href="javascript:showTab(\''.$error["tab"].'\');">'.atktext(array("tab_".$error["tab"], $error["tab"]),$node->m_module, $node->m_type).'</a> )';
            else $error_tab = "";

            if(!is_array($error['attrib_name']))
            {
              $label = atktext($error['attrib_name'], $node->m_module, $type);
            }
            else
            {
              $label = array();
              foreach($error['attrib_name'] as $attrib)
                $label[] = atktext($attrib, $node->m_module, $type);

              $label= implode(", ", $label);
            }

            $errors[] = array("msg"=>$error['msg'].$error_tab, "label"=>$label);
          }
        }
        if (count($pk_err_attrib)>0) // Make primary key error message
        {
          for($i=0;$i<count($pk_err_attrib); $i++)
          {
            $pk_err_msg .= atktext($pk_err_attrib[$i], $node->m_module);
            if (($i+1) < count($pk_err_attrib)) $pk_err_msg .= ", ";
          }
          $errors[] = array("label"=>atktext("error_primarykey_exists"),
                            "msg"=>$pk_err_msg);
        }
      }*/

      // Handle fields
      // load images
      $theme = &atkinstance("atk.ui.atktheme");
      $tipimg = $theme->imgPath("help.gif");
      $reqimg = '<img align="top" src="'.$theme->imgPath("required_field.gif").'" border="0"
                  alt="'.atktext("field_obligatory").'" title="'.atktext("field_obligatory").'">';

      /* display the edit fields */
      $fields = array();
      $tab = $this->getDefaultTab();

      for ($i = 0, $_i= count($data["fields"]); $i<$_i; $i++)
      {
        $field = &$data["fields"][$i];
        $tplfield = array();

        $tplfield["tab"] =  $field["tabs"];

        $tplfield["initial_on_tab"] = $tplfield["tab"]==$tab;

        $tplfield["class"] = "tabbedPaneAttr tabbedPaneTab{$field['tabs']}";

        // Check if there are attributes initially hidden on this tabbedpane
        if ($field["attribute"]->isInitialHidden($defaults))
            $tplfield["class"].= " atkAttrRowHidden";

        $tplfield["rowid"] = "tabbedPaneAttr_".($field['id']!=''?$field['id']:getUniqueID("anonymousattribrows")); // The id of the containing row

        /* check for separator */
        if ($field["html"] == "-" && $i > 0 && $data["fields"][$i-1]["html"] != "-")
        {
          $tplfield["line"] = "<hr>";
        }
        /* double separator, ignore */
        elseif ($field["html"] == "-")
        {
        }
        /* only full HTML */
        elseif (isset($field["line"]))
        {
          $tplfield["line"] = $field["line"];
        }
        /* edit field */
        else
        {
          if ($field["attribute"]->m_ownerInstance->getNumbering())
          {
            atkViewEditBase::_addNumbering($field, $tplfield, $i);
          }

          /* does the field have a label? */
          if ((isset($field["label"]) && $field["label"]!=="AF_NO_LABEL") && !$this->isAttributeSingleOnTab($field['name'])
                              || !isset($field["label"]))
          {
            if ($field["label"] == "")
            {
              $tplfield["label"] = "";
            }
            else
            {
              $tplfield["label"] = $field["label"];
              if ($field["error"]) // TODO KEES
              {
                $tplfield["error"] = $field["error"];
              }
            }
          }
          else
          {
            $tplfield["label"]="AF_NO_LABEL";
          }

          /* obligatory indicator */
          if ($field["obligatory"])
          {
            $tplfield["label"];
            $tplfield["obligatory"] = $reqimg;
          }

          /* html source */
          $tplfield["widget"] = $field["html"];
          $editsrc = $field["html"];

          /* tooltip */
          
          if (is_object($node->m_attribList[$field['name']]))
          {
            $module = $node->m_attribList[$field['name']]->getModule();
          }

          $editsrc.= $this->getToolTipForAttribute("$module", $field["name"]);

          $tplfield['id']=str_replace('.','_',$node->atknodetype().'_'.$field["id"]);

          $tplfield["full"] = $editsrc;
        }
        $fields[] = $tplfield; // make field available in numeric array
        $params[$field["name"]] = $tplfield; // make field available in associative array
      }

      $ui = &$node->getUi();
      $page = &$node->getPage();

      $result = "";

      foreach ($arr["hide"] as $hidden)
      {
        $result.= $hidden;
      }

      $params["activeTab"] = $tab;
      $params["panename"] = $this->m_name;
      $params["fields"] = $fields; // add all fields as an numeric array.
      $params["errortitle"] = $error_title;
      $params["errors"] = $errors; // Add the list of errors.
      if (!$template) $template = $node->getTemplate($mode, $record, $tab);
      $result .= $ui->render("tabbededitform.tpl", $params);

      $content = $this->tabulate($mode, $result, $fieldprefix);

      return $content;
    }



    /**
    * Display a tabbed pane with attributes
    * @param $record  Array with fields
    * @return html code
    */
    function display($record, $mode="")
    {
      // get active tab
      $active_tab = $this->getDefaultTab();
      $fields = array();

      $node = &$this->m_ownerInstance;
      $ui = &$node->getUi();

      // For all attributes we use the display() function to display the
      // attributes current value. This may be overridden by supplying
      // an <attributename>_display function in the derived classes.
      foreach ($this->m_attribsList as $name=>$tab)
      {
        $p_attrib = &$node->getAttribute($name);
        if(is_object($p_attrib))
        {
          $tplfield = array();
          if (!$p_attrib->hasFlag(AF_HIDE_VIEW))
          {
            $fieldtab = $this->m_attribsList[$name];

            $tplfield["class"] = "tabbedPaneAttr tabbedPaneTab{$fieldtab}";
            $tplfield["rowid"] = "tabbedPaneAttr_".getUniqueID("anonymousattribrows"); // The id of the containing row
            $tplfield["tab"] = $tplfield["class"]; // for backwards compatibility

            $tplfield["initial_on_tab"] = ($fieldtab==$active_tab);

            // An <attributename>_display function may be provided in a derived
            // class to display an attribute. If it exists we will use that method
            // else we will just use the attribute's display method.
            $funcname = $p_attrib->m_name."_display";
            if (method_exists($node, $funcname)) $editsrc = $node->$funcname($record, "view");
            else $editsrc=$p_attrib->display($record, "view");

            /* tooltip */
            $module = $p_attrib->getModule();

            $editsrc.= $this->getToolTipForAttribute("$module", $name);
            
            $tplfield["full"] = $editsrc;
            $tplfield["widget"] = $editsrc; // in view mode, widget and full are equal

            // The Label of the attribute (can be suppressed with AF_NOLABEL or AF_BLANKLABEL)
            // For each attribute, a txt_<attributename> must be provided in the language files.
            if (!$p_attrib->hasFlag(AF_NOLABEL) && !$this->isAttributeSingleOnTab($name))
            {
              if ($p_attrib->hasFlag(AF_BLANKLABEL))
              {
                $tplfield["label"] = "";
              }
              else
              {
                $tplfield["label"] = $p_attrib->label($record);
              }
            }
            else
            {
              // Make the rest fill up the entire line
              $tplfield["label"] = "";
              $tplfield["line"] = $tplfield["full"];
            }
            $fields[] = $tplfield;
          }
        }
        else
        {
          atkerror("Attribute $name not found!");
        }
      }
      $innerform = $ui->render($node->getTemplate("view", $record, $tab), array("fields"=>$fields));

      return $this->tabulate("view", $innerform);
    }

Add the following methods

getToolTip overwrites the tabed attributes tooltip (probably don't want help on a series of tabs but maybe you do.)

    function getToolTip() {
      return null;
    }

    function getToolTipForAttribute($module="atk", $name) {
    
          $node = &$this->m_ownerInstance;
    
          $nodetxt = atktext($node->m_type, $module, "", "", "", true);
          if (!$nodetxt) {
            $nodetxt = $node->m_type;
          }
          
          $attributetxt = atktext($name, $module, "", "", "", true);      
          if (!$attributetxt) {
            $attributetxt = $name;
          }
          
          $link = $nodetxt . " - " . $attributetxt;
          
          $tooltip = ' ';
          $tooltip .= '<a href="';
          $tooltip .= atkconfig("help_site");
          $tooltip .= $link;
          $tooltip .= '" target="help">?</a>';
    
          return $tooltip;
          
    }
Personal tools
Navigation