/**
  * given a podio field object -> returns a human readable format of the field value (or app reference as array (app_id, item_id)
  * @param type $field should be of type PodioItemField
  * @return string
  */
 public static function getFieldValue($field)
 {
     $value = "";
     if ($field->type == "text" || $field->type == "number" || $field->type == "progress" || $field->type == "duration" || $field->type == "state") {
         if (is_string($field->values)) {
             // TODO refactor
             $value = $field->values;
             $value = str_ireplace('</p>', '</p><br>', $value);
             $value = preg_replace('/\\<br(\\s*)?\\/?\\>/i', "\n", $value);
             $value = strip_tags(br2nl($value));
             $value = str_replace('&nbsp;', ' ', $value);
             $value = str_replace('&amp;', '&', $value);
         } else {
             if (is_numeric($field->values)) {
                 $value = $field->values;
             } else {
                 echo "WARN expected string or number, but found: ";
                 var_dump($field->values);
             }
         }
         return $value;
     }
     if ($field->type == "category") {
         $selected_categories = array();
         foreach ($field->values as $category) {
             array_push($selected_categories, $category['text']);
         }
         return HumanFormat::implodeStrings(", ", $selected_categories);
     }
     if ($field->type == "date") {
         return HumanFormat::parseDate($field->values['start']) . " - " . HumanFormat::parseDate($field->values['end']);
     }
     if ($field->type == "money") {
         return "" . $field->values['value'] . " " . $field->values['currency'];
     }
     if ($field->type == "contact") {
         if (is_array($field->values) || $field->values instanceof Traversable) {
             foreach ($field->values as $contact) {
                 $value .= "\nUserid: {$contact->user_id}, name: {$contact->name}, email: " . HumanFormat::implodeStrings(', ', $contact->mail) . ", phone: " . HumanFormat::implodeStrings(", ", $contact->phone);
             }
         } else {
             echo "WARN unexpected contact type:";
             var_dump($field);
         }
         return $value;
     }
     if ($field->type == "embed") {
         if (is_array($field->values) || $field->values instanceof Traversable) {
             foreach ($field->values as $embed) {
                 //TODO implode (general function..)
                 $value .= "\nurl: " . $embed->original_url;
             }
         } else {
             echo "WARN unexpected embed type:";
             var_dump($field);
         }
         return $value;
     }
     if ($field->type == "app") {
         if (is_array($field->values) || $field->values instanceof Traversable) {
             foreach ($field->values as $app) {
                 //TODO implode (general function..)
                 $value .= "\napp: " . $app->app->name . ", item_name: " . $app->item_name . ", title: " . $app->title;
                 //TODO more info?!
             }
         } else {
             echo "WARN unexpected app type:";
             var_dump($field);
         }
         return $value;
     }
     if ($field->type == "image") {
         $images = array();
         foreach ($field->values as $image) {
             array_push($images, "fileid: " . $image->file_id . ", name: " . $image->name);
         }
         return HumanFormat::implodeStrings(" | ", $images);
     }
     if ($field->type == "location") {
         $locations = array();
         foreach ($field->values as $location) {
             array_push($locations, $location);
         }
         return HumanFormat::implodeStrings(" | ", $locations);
     }
     echo "WARN unexpected type: ";
     var_dump($field);
     return $field->values;
 }
/**
 * Backups $app to a subfolder in $path
 * 
 * @param type $app app to backup
 * @param type $path in this folder a subfolder for the app will be created
 */
function backup_app($app, $path, $downloadFiles)
{
    $path_app = $path . '/' . fixDirName($app->config['name']);
    global $verbose;
    if ($verbose) {
        echo "App: " . $app->config['name'] . "\n";
        echo "debug: MEMORY: " . memory_get_usage(true) . " | " . memory_get_usage(false) . "\n";
    }
    mkdir($path_app);
    $appFile = "";
    $appFiles = array();
    $files_in_app_html = "<html><head><title>Files in app: " . $app->config['name'] . "</title></head><body>" . "<table border=1><tr><th>name</th><th>link</th><th>context</th></tr>";
    try {
        #$appFiles = PodioFile::get_for_app($app->app_id, array('attached_to' => 'item'));
        $appFiles = PodioFetchAll::iterateApiCall('PodioFile::get_for_app', $app->app_id, array(), FILE_GET_FOR_APP_LIMIT);
        #var_dump($appFiles);
        PodioFetchAll::flattenObjectsArray($appFiles, PodioFetchAll::podioElements(array('file_id' => null, 'name' => null, 'link' => null, 'hosted_by' => null, 'context' => array('id' => NULL, 'type' => null, 'title' => null))));
        if ($verbose) {
            echo "fetched information for " . sizeof($appFiles) . " files in app.\n";
        }
    } catch (PodioError $e) {
        show_error($e);
    }
    try {
        $allitems = PodioFetchAll::iterateApiCall('PodioItem::filter', $app->app_id, array(), ITEM_FILTER_LIMIT, 'items');
        echo "app contains " . sizeof($allitems) . " items.\n";
        for ($i = 0; $i < sizeof($allitems); $i += ITEM_XLSX_LIMIT) {
            $itemFile = PodioItem::xlsx($app->app_id, array("limit" => ITEM_XLSX_LIMIT, "offset" => $i));
            RateLimitChecker::preventTimeOut();
            file_put_contents($path_app . '/' . $app->config['name'] . '_' . $i . '.xlsx', $itemFile);
            unset($itemFile);
        }
        $before = time();
        gc_collect_cycles();
        echo "gc took : " . (time() - $before) . " seconds.\n";
        foreach ($allitems as $item) {
            if ($verbose) {
                echo " - " . $item->title . "\n";
            }
            $folder_item = fixDirName($item->item_id . '_' . $item->title);
            $path_item = $path_app . '/' . $folder_item;
            mkdir($path_item);
            unset($itemFile);
            $itemFile = HumanFormat::toHumanReadableString($item);
            if ($downloadFiles) {
                foreach ($appFiles as $file) {
                    if ($file->context['type'] == 'item' && $file->context['id'] == $item->item_id) {
                        $link = downloadFileIfHostedAtPodio($path_item, $file);
                        # $link is relative to $path_item (if downloaded):
                        if (!preg_match("/^http/i", $link)) {
                            $link = RelativePaths::getRelativePath($path_app, $path_item . '/' . $link);
                        }
                        $itemFile .= "File: {$link}\n";
                        $files_in_app_html .= "<tr><td>" . $file->name . "</td><td><a href=\"" . $link . "\">" . $link . "</a></td><td>" . $file->context['title'] . "</td></tr>";
                    }
                }
            }
            //TODO refactor to use less api calls: (not possible??!)
            if ($item->comment_count > 0) {
                #echo "comments.. (".$item->comment_count.")\n";
                $comments = PodioComment::get_for('item', $item->item_id);
                RateLimitChecker::preventTimeOut();
                $commentsFile = "\n\nComments\n--------\n\n";
                foreach ($comments as $comment) {
                    $commentsFile .= 'by ' . $comment->created_by->name . ' on ' . $comment->created_on->format('Y-m-d at H:i:s') . "\n----------------------------------------\n" . $comment->value . "\n\n\n";
                    if ($downloadFiles && isset($comment->files) && sizeof($comment->files) > 0) {
                        foreach ($comment->files as $file) {
                            $link = downloadFileIfHostedAtPodio($path_item, $file);
                            # $link is relative to $path_item (if downloaded):
                            if (!preg_match("/^http/i", $link)) {
                                $link = RelativePaths::getRelativePath($path_app, $path_item . '/' . $link);
                            }
                            $commentsFile .= "File: {$link}\n";
                            $files_in_app_html .= "<tr><td>" . $file->name . "</td><td><a href=\"" . $link . "\">" . $link . "</a></td><td>" . $file->context['title'] . "</td></tr>";
                        }
                    }
                }
            } else {
                $commentsFile = "\n\n[no comments]\n";
                #echo "no comments.. (".$item->comment_count.")\n";
            }
            file_put_contents($path_item . '/' . fixDirName($item->item_id . '-' . $item->title) . '.txt', $itemFile . $commentsFile);
            $appFile .= $itemFile . "\n\n";
        }
        //store non item/comment files:
        if ($verbose) {
            echo "storing non item/comment files..\n";
        }
        $app_files_folder = 'other_files';
        $path_app_files = $path_app . '/' . $app_files_folder;
        mkdir($path_app_files);
        $files_in_app_html .= "<tr><td><b>App Files</b></td><td><a href={$app_files_folder}>" . $app_files_folder . "</a></td><td></td></tr>";
        foreach ($appFiles as $file) {
            if ($file->context['type'] != 'item' && $file->context['type'] != 'comment') {
                echo "debug: downloading non item/comment file: {$file->name}\n";
                $link = downloadFileIfHostedAtPodio($path_app_files, $file);
                # $link is relative to $path_item (if downloaded):
                if (!preg_match("/^http/i", $link)) {
                    $link = RelativePaths::getRelativePath($path_app, $path_item . '/' . $link);
                }
                $files_in_app_html .= "<tr><td>" . $file->name . "</td><td><a href=\"" . $link . "\">" . $link . "</a></td><td>" . $file->context['title'] . "</td></tr>";
            }
        }
    } catch (PodioError $e) {
        show_error($e);
        $appFile .= "\n\nPodio Error:\n" . $e;
    }
    file_put_contents($path_app . '/all_items_summary.txt', $appFile);
    $files_in_app_html .= "</table></body></html>";
    file_put_contents($path_app . "/files_in_app.html", $files_in_app_html);
    unset($appFile);
    unset($files_in_app_html);
}