Index: common/db/tags.php
===================================================================
--- common/db/tags.php	(revision 1080)
+++ common/db/tags.php	(working copy)
@@ -53,6 +53,23 @@
     return db_fetch_all($query);
 }
 
+// Receives an array of tag_id's
+// Return an array with id's of their parents
+// Each parent id appears only once
+function tag_get_parents($tags) {
+    $query = sprintf("SELECT DISTINCT(`parent`)
+            FROM ia_tags
+            WHERE `id` IN (%s)",
+            implode(",", array_map('db_quote', $tags))
+        );
+    $result = db_fetch_all($query);
+    $ret = Array();
+    foreach ($result as $row) {
+        $ret[] = $row['parent'];
+    }
+    return $ret;
+}
+
 // Get tag id for a certain tag
 function tag_get_id($tag) {
     log_assert(is_tag($tag));
@@ -80,6 +97,17 @@
     return db_fetch_all($query);
 }
 
+// Get a list of tags from a list of tag ids
+function tag_get_by_ids($tag_ids) {
+    $query = sprintf(
+            "SELECT `id` AS tag_id, `name` AS tag_name,
+                    `parent` AS tag_parent, `type` AS tag_type
+            FROM ia_tags
+            WHERE `id` IN (%s)", implode(', ', array_map('db_quote', $tag_ids))
+        );
+    return db_fetch_all($query);
+}
+
 // Assign numeric id to a given tag name
 function tag_assign_id($tag) {
     log_assert(is_tag($tag));
@@ -161,7 +189,7 @@
     return $result['cnt'];
 }
 
-// Clear all tags, filtered by type
+// Clear all tags, filtered by type and parent
 function tag_clear($obj, $obj_id, $type = null) {
     log_assert(is_taggable($obj));
     log_assert(is_null($type) || is_tag_type($type));
@@ -169,12 +197,13 @@
     $query = sprintf("DELETE FROM ia_%s_tags WHERE %s AND tag_id IN (%s)",
         db_escape($obj), $where, "%s"
     );
-    if (is_null($type)) {
-        $join = "";
-    } else {
+
+    $join = "";
+    if (!is_null($type)) {
         $join = "LEFT JOIN ia_tags AS tags ON obj_tags.tag_id = tags.id";
         $where .= sprintf(" AND tags.type = %s", db_quote($type));
     }
+
     $subquery = sprintf("SELECT tag_id FROM ia_%s_tags AS obj_tags %s WHERE %s",
         db_escape($obj), $join, $where);
     $tag_ids = array();
Index: common/tags.php
===================================================================
--- common/tags.php	(revision 1080)
+++ common/tags.php	(working copy)
@@ -51,6 +51,28 @@
     return implode(", ", $tag_names);
 }
 
+// Receives a list of parent tags and children tags
+// For each parent_tag adds an array 'sub_tags' containing
+// an array with all his children tags
+function build_tags_tree($parent_tags, $sub_tags) {
+    log_assert(is_array($parent_tags), "Parent tags is not an array");
+    log_assert(is_array($sub_tags), "Children tags is not an array");
+
+    $parent_tags_key = Array();
+    foreach ($parent_tags as $key => $tag) {
+        $parent_tags[$key]['sub_tags'] = Array();
+        $parent_tags_key[ $tag['tag_id'] ] = $key;
+    }
+
+    foreach ($sub_tags as $tag) {
+        log_assert(isset($parent_tags_key[ $tag['tag_parent'] ]), "Child tag doesn't have a parent");
+        $parent_tag_key = $parent_tags_key[ $tag['tag_parent'] ];
+        $parent_tags[ $parent_tag_key ]['sub_tags'][] = $tag;
+    }
+
+    return $parent_tags;
+}
+
 // Receives a list of tags of a certain type and, optionally, with a
 // certain parent and updates the tag list for the specified object.
 // Returns a list of tag ids.
Index: common/task.php
===================================================================
--- common/task.php	(revision 1080)
+++ common/task.php	(working copy)
@@ -276,4 +276,22 @@
     return $task_page["forum_topic"];
 }
 
+// Receives a list of method and algorithm tag ids and adds links them to task_id
+function task_update_tags($task_id, $method_tags_id, $algorithm_tags_id) {
+    log_assert(is_array($method_tags_id), 'method_tags must be an array');
+    log_assert(is_array($algorithm_tags_id), 'algorithm_tags must be an array');
+    log_assert(is_task_id($task_id), "Invalid task_id");
+
+    tag_clear('task', $task_id, 'method');
+    tag_clear('task', $task_id, 'algorithm');
+
+    foreach ($method_tags_id as $tag_id) {
+        tag_add('task', $task_id, $tag_id);
+    }
+
+    foreach ($algorithm_tags_id as $tag_id) {
+        tag_add('task', $task_id, $tag_id);
+    }
+}
+
 ?>
Index: www/controllers/task.php
===================================================================
--- www/controllers/task.php	(revision 1080)
+++ www/controllers/task.php	(working copy)
@@ -264,5 +264,66 @@
     redirect(url_home());
 }
 
+// Tag a task
+function controller_task_tag($task_id) {
+    if (!is_task_id($task_id)) {
+        flash_error("Problema inexistenta");
+        redirect(url_home());
+    }
 
+    $task = task_get($task_id);
+    identity_require('task-tag', $task);
+
+    if (request_is_post()) {
+        $algorithm_tags_id = request("algorithm_tags");
+        $method_tags_id = tag_get_parents($algorithm_tags_id);
+
+        if (!is_array($algorithm_tags_id)) {
+            flash_error("Datele trimise sunt invalide. Raporteaza aceasta problema unui admin.");
+            redirect(url_task_edit_tag($task_id));
+        }
+
+        foreach ($algorithm_tags_id as $tag_id) {
+            if (!is_tag_id($tag_id)) {
+                flash_error("Datele trimise sunt invalide. Raporteaza aceasta problema unui admin.");
+                redirect(url_task_edit_tag($task_id));
+            }
+        }
+
+        task_update_tags($task_id, $method_tags_id, $algorithm_tags_id);
+        flash("Tagurile au fost salvate cu succes");
+    }
+
+    $tags = tag_get_all( Array('method') );
+    $sub_tags = tag_get_all( Array('algorithm') );
+
+    // Build_tags_tree looks or tag_id, tag_name etc.
+    // tag_get_all return id, name, etc.
+    foreach ($tags as &$tag) {
+        $tag['tag_id'] = $tag['id'];
+        $tag['tag_name'] = $tag['name'];
+        $tag['tag_type'] = $tag['type'];
+        $tag['tag_parent'] = $tag['parent'];
+    }
+
+    // Same for subtags
+    foreach ($sub_tags as &$tag) {
+        $tag['tag_id'] = $tag['id'];
+        $tag['tag_name'] = $tag['name'];
+        $tag['tag_type'] = $tag['type'];
+        $tag['tag_parent'] = $tag['parent'];
+    }
+
+    $tags_tree = build_tags_tree($tags, $sub_tags);
+
+    // Get tags for task_id
+    $task_tags = tag_get('task', $task_id, 'algorithm');
+
+    $view['title'] = "Editeaza tagurile pentru problema ".$task['title'];
+    $view['task'] = $task;
+    $view['tags_tree'] = $tags_tree;
+    $view['task_tags'] = $task_tags;
+    execute_view_die('views/task_tag_edit.php', $view);
+}
+
 ?>
Index: www/url.php
===================================================================
--- www/url.php	(revision 1080)
+++ www/url.php	(working copy)
@@ -252,6 +252,11 @@
     return url_complex("admin/problema/$task_id");
 }
 
+function url_task_edit_tags($task_id) {
+    log_assert(is_task_id($task_id));
+    return url_complex("admin/problema/{$task_id}", array( 'action' => 'tag-edit'));
+}
+
 function url_task_create() {
     return url_complex("admin/problema-noua");
 }
Index: www/views/task_tag_edit.php
===================================================================
--- www/views/task_tag_edit.php	(revision 0)
+++ www/views/task_tag_edit.php	(revision 0)
@@ -0,0 +1,33 @@
+<?php
+    include('header.php');
+    $task_link = url_task($view['task']['id']);
+    $task_title = $view['task']['title'];
+    $action_link = url_task_edit_tags($view['task']['id']);
+    $task_tags = $view['task_tags'];
+?>
+<h1>Taguri pentru problema <a href="<?= $task_link ?>"><?= $task_title ?></a></h1>
+<form name="task_tags" action="<?= $action_link ?>" method="post">
+<?php
+    $tags_tree = $view['tags_tree'];
+    foreach ($tags_tree as $tag) {
+        echo '<h3>'.$tag['tag_name'].'</h3><ul style="list-style-type: none">';
+        foreach ($tag['sub_tags'] as $sub_tag) {
+            // Check if tag is already assigned to this task
+            $checked = '';
+            foreach ($task_tags as $task_tag) {
+                if ($task_tag['tag_id'] == $sub_tag['tag_id']) {
+                    $checked = "checked";
+                }
+            }
+            echo '<li>
+                    <input type="checkbox" '.$checked.' name="algorithm_tags[]" value="'.$sub_tag['tag_id'].'"/>'
+                    .$sub_tag['tag_name'].
+                '</li>';
+        }
+        echo "</ul>";
+    }
+?>
+<input type="submit" class="button important" value="Submit" />
+</form>
+
+<?php include('footer.php'); ?>
