diff --git a/lib/Fins.pmod/Helpers.pmod/Macros.pmod/Basic.pike b/lib/Fins.pmod/Helpers.pmod/Macros.pmod/Basic.pike --- a/lib/Fins.pmod/Helpers.pmod/Macros.pmod/Basic.pike +++ b/lib/Fins.pmod/Helpers.pmod/Macros.pmod/Basic.pike @@ -1,821 +1,833 @@ inherit .Base; object localeLogger = Tools.Logging.get_logger("fins.macros.LOCALE"); object logger = Tools.Logging.get_logger("fins.macros.Basic"); Tools.Mapping.MappingCache feed_data = Tools.Mapping.MappingCache(600); //! string simple_macro_sessionid(Fins.Template.TemplateData data, mapping|void args) { return data->get_request()->misc->session_id; } //! args id, string string simple_macro_LOCALE(Fins.Template.TemplateData data, mapping|void args) { object r = data->get_request(); string t; if(!r) { t = args["string"]; } else { if(!objectp(r)) // a real request will be an object, otherwise we won't be able to make Locale work. { localeLogger->debug("returning string without Locale translation. request=%O, args=%O", r, args); return args["string"]; } mixed e; if(e = catch(t = Locale.translate(r->get_project(), r->get_lang(), (int)args["id"], args["string"]))) localeLogger->exception(sprintf("LOCALE macro failed: %O, args: %O\n", r, args), e); } return t; } +//! args: var | options +string simple_macro_val_from_options(Fins.Template.TemplateData data, mapping|void args) +{ + if(!args->var || !args->options) return "val_from_options missing arguments"; + + foreach(args->options;int o; mixed v) + { + if(v["value"] == args->var) + return v["name"]; + } +} + //! args: var string simple_macro_humanize(Fins.Template.TemplateData data, mapping|void args) { // werror("humanize: %O\n", args->var); return Tools.Language.Inflect.humanize(args->var || ""); } string simple_macro_set(Fins.Template.TemplateData data, mapping|void args) { data->add(args->var, get_var_value(args->val, data)); return ""; } //! args: string simple_macro_dump_data(Fins.Template.TemplateData data, mapping|void args) { return sprintf("%O\n", mkmapping(indices(data->get_data()), values(data->get_data()))); } //! args: string simple_macro_dump_id(Fins.Template.TemplateData data, mapping|void args) { return sprintf("%O\n", mkmapping(indices(data->get_request()), values(data->get_request()))); } //! populate a data field with a mapping containing available language codes (keys) and native names (values) //! //! args: name string simple_macro_available_languages(Fins.Template.TemplateData data, mapping|void args) { // we do this to force a language update, if it hasn't happened already. string lang = data->get_request()->get_lang(); data->get_data()[args->name] = app->available_languages(); return ""; } //! produce a drop down language selector //! //! args: text string simple_macro_language_selector(Fins.Template.TemplateData data, mapping|void args) { String.Buffer buf = String.Buffer(); // we do this to force a language update, if it hasn't happened already. string lang = data->get_request()->get_lang(); mapping l = app->available_languages(); buf += "
\n"; buf += (args->text || "Language: "); buf += ""; buf += "\n
\n"; return buf->get(); } //! args: controller, action, args, _id=dom element id //! //! any arguments other than those above will be considered variables to //! be added to the url above. string simple_macro_action_link(Fins.Template.TemplateData data, mapping|void args) { object controller; object request = data->get_request(); string event = "index"; if(args->action) event = args->action; // if(!event) throw(Error.Generic("action_link: event name must be provided.\n")); catch(controller = request->controller); if(args->controller) controller = app->get_controller_for_path(args->controller, controller); if(!controller) throw(Error.Generic("action_link: controller " + args->controller + " can not be resolved.\n")); mixed action = controller[event]; if(!action) throw(Error.Generic("action_link: action " + args->action + " can not be resolved.\n")); array uargs; if(args->args) uargs = ((string)args->args)/"/"; string target = (args["#"]?("#" + args["#"]):""); string id, title; if(args->_id) id = args->_id; if(args->_title) title = args->_title; m_delete(args, "_id"); m_delete(args, "_title"); m_delete(args, "controller"); m_delete(args, "action"); m_delete(args, "args"); m_delete(args, "#"); string url = app->url_for_action(action, uargs, args); return ""):">"); } //! args: controller, action, args, method, enctype //! //! any arguments other than those above will be considered variables to //! be added to the url above. string simple_macro_action_form(Fins.Template.TemplateData data, mapping|void args) { object controller; object request = data->get_request(); string event = "index"; if(args->action) event = args->action; // if(!event) throw(Error.Generic("action_form: event name must be provided.\n")); controller = request->controller; if(args->controller) controller = app->get_controller_for_path(args->controller, controller); if(!controller) throw(Error.Generic("action_form: controller " + args->controller + " can not be resolved.\n")); mixed action = controller[event]; if(!action) throw(Error.Generic("action_form: action " + args->action + " can not be resolved.\n")); array uargs; if(args->args) uargs = args->args/"/"; string other = ""; if(args->method) other += " method=\"" + args->method + "\""; if(args->enctype) other += " enctype=\"" + args->enctype + "\""; if(args->id) other += " id=\"" + args->id + "\""; m_delete(args, "controller"); m_delete(args, "action"); m_delete(args, "args"); m_delete(args, "method"); m_delete(args, "enctype"); m_delete(args, "id"); string url = app->url_for_action(action, uargs, args); return "
"; } //! args: controller, action, args //! //! any arguments other than those above will be considered variables to //! be added to the url above. string simple_macro_action_url(Fins.Template.TemplateData data, mapping|void args) { object controller; //werror("******* action_url\n"); object request = data->get_request(); string event = args->action; // if(!event) throw(Error.Generic("action_link: event name must be provided.\n")); controller = request->controller; if(args->controller) controller = app->get_controller_for_path(args->controller, controller); if(!controller) throw(Error.Generic("action_url: controller " + args->controller + " can not be resolved.\n")); mixed action = controller[event||"index"]; if(!action) throw(Error.Generic("action_url: action " + args->action + " can not be resolved.\n")); //werror("********* action: %O\n", action); array uargs; if(args->args) uargs = args->args/"/"; string target = (args["#"]?("#" + args["#"]):""); m_delete(args, "controller"); m_delete(args, "action"); m_delete(args, "args"); m_delete(args, "#"); string url = app->url_for_action(action, uargs, args) + target; return url; } //! args: none required, arg "mandatory" may be specified, and args "name", //! "value" and "checked" enable special functionality, below. //! //! generates an input tag with any args passed along. //! if value in the request's variables mapping with the same name is present, //! it will be used to fill the default value, overriding the default passed //! as the "value" argument. //! //! if field of type "checkbox" or "radio", and the argument "checked" is present //! with a value of "1", the input will be "activated". "data_supplied" is a value that, //! if present disables the checked argument for checkboxes (since unchecked boxes don't //! provide a value when submitted, it appears to this control as if no value were provided) string simple_macro_input(Fins.Template.TemplateData data, mapping|void args) { //werror("******* input\n"); object request = data->get_request(); string event = args->action; // if(!event) throw(Error.Generic("input: event name must be provided.\n")); mixed v; int checked; if(!args) args = ([]); string type = lower_case(args->type||""); if(!(<"radio", "checkbox">)[type] && args->name && ((v = request->variables[args->name]) && !arrayp(v))) args->value = (string)v; String.Buffer buf = String.Buffer(); buf->add(")[type]) { if(lower_case(s) == "checked") { checked = 1; continue; } if(lower_case(s) == "data_supplied") continue; } buf->add(" " + s + "=\"" + v + "\""); } // werror("is %O(%O) == %O?", args->name, args->value, request->variables[args->name]); if((<"radio", "checkbox">)[type] && ((request->variables[args->name] == (string)args->value) || (arrayp(request->variables[args->name]) && search(request->variables[args->name], (string)args->value)!=-1)) || (checked && !args->data_supplied)) { buf->add(" checked=\"1\""); } buf->add("/>"); if(args->mandatory && lower_case(args->mandatory) != "false") { if(!args->value || !sizeof(args->value)) { buf->add(""); } } return buf->get(); } //! args: none required, args "mandatory", "default_value", "options" may be specified //! //! options may point to an array of strings or an array of 2 element arrays, where the first element //! is the value name and the second element is the value to display to the user in the drop-down. //! //! generates a select tag with any args passed along //! and a value in the request's variables mapping used to fill the default value string simple_macro_select(Fins.Template.TemplateData data, mapping|void args) { object request = data->get_request(); string event = args->action; mixed v; String.Buffer buf = String.Buffer(); if(!args) args = ([]); if(args->name && (v = request->variables[args->name])) args->value = v; array valid_options = args->options; string value = args->value || args->default_value; m_delete(args, "value"); m_delete(args, "default_value"); m_delete(args, "options"); buf->add("add(" " + s + "=\"" + v + "\""); } buf->add("/>\n"); werror("options: %O\n", valid_options); foreach(valid_options;; string|array vo) { string dn = ""; // display name string vn = ""; // value name if(arrayp(vo)) { dn = vo[1]; vn = vo[0]; } else { vn = vo; dn = vo; } buf->add("\n"); } buf->add("\n"); if(args->mandatory && lower_case(args->mandatory) != "false") { if(!args->value || !sizeof(args->value)) { buf->add(""); } } return buf->get(); } //! args: none required, arg "mandatory" may be specified //! //! generates a textarea with any args passed along //! and a value in the request's variables mapping used to fill the default value string simple_macro_textarea(Fins.Template.TemplateData data, mapping|void args) { object request = data->get_request(); string event = args->action; mixed v; if(!args) args = ([]); if(args->name && (v = request->variables[args->name])) args->value = v; string value = args->value || ""; m_delete(args, "value"); String.Buffer buf = String.Buffer(); buf->add("add(" " + s + "=\"" + v + "\""); } buf->add("/>"); buf->add(value); buf->add(""); if(args->mandatory && lower_case(args->mandatory) != "false") { if(!args->value || !sizeof(args->value)) { buf->add(""); } } return buf->get(); } //! args: var string simple_macro_autoformat(Fins.Template.TemplateData data, mapping|void args) { return replace(args->var||"", ({"\n\n", "\n"}), ({"

", "
"})); } //! args: var string simple_macro_capitalize(Fins.Template.TemplateData data, mapping|void args) { return String.capitalize(args->var||""); } //! args: var //! if var is not provided, it is assumed to be "msg". string simple_macro_flash(Fins.Template.TemplateData data, mapping|void args) { if(!args->var) args->var = "msg"; return (data->get_flash()[args->var]||""); } //! args: var string simple_macro_sizeof(Fins.Template.TemplateData data, mapping|void args) { return (string)(sizeof(args->var ||({}))); } //! args: var, splice, final string simple_macro_implode(Fins.Template.TemplateData data, mapping|void args) { mixed v = args->var; if(!arrayp(v)) return "invalid type for " + args->var; string retval = ""; if(args->nice) { retval = String.implode_nicely(v, args->nice); } else { retval = v*args->final; } return retval; } //! args: var string simple_macro_boolean(Fins.Template.TemplateData data, mapping|void args) { mixed v = args->var; if (intp(v)) { return (v != 0)?"Yes":"No"; } else if(stringp(v)) { return ((int)v != 0)?"Yes":"No"; } else { return "invalid type for boolean "; } } //! args: var string simple_macro_describe_object(Fins.Template.TemplateData data, mapping|void args) { mixed v = args->var; if(objectp(v) && v->describe) return v->describe(); else return sprintf("%O\n", v); } //! args: var string simple_macro_describe(Fins.Template.TemplateData data, mapping|void args) { string key = args->key; mixed value = args->var; string rv = ""; if(stringp(value) || intp(value)) rv += value; else if(arrayp(value)) rv += describe_array(0, key, value); else if(objectp(value)) rv += describe_object(0, key, value); return rv; } //! display a calendar object as a distance in the past in a friendly manner //! //! args: var string simple_macro_friendly_date(Fins.Template.TemplateData data, mapping|void args) { mixed p = args->var; if(intp(p)) p = Calendar.Second(p); return Tools.String.friendly_date(p); } //! display a calendar object in a friendly manner using a format appropriate to the //! time interval the calendar object represents (week, month, second, etc) //! //! args: var string simple_macro_describe_date(Fins.Template.TemplateData data, mapping|void args) { mixed p = args->var; if(intp(p)) p = Calendar.Second(p); return Tools.String.describe_date(p); } //! display a calendar object as a date and time in a friendly manner //! //! args: var string simple_macro_describe_datetime(Fins.Template.TemplateData data, mapping|void args) { mixed p = args->var; if(intp(p)) p = Calendar.Second(p); if(p && p->format_ext_time_short) return p->format_ext_time_short(); else return "N/A"; } //! provides the context root of this application, if any //! string simple_macro_context_root(Fins.Template.TemplateData data, mapping|void args) { return app->context_root; } //! args: var, format //! where var is a calendar object or a unix timestamp. format is a Calendar object format type; default is ext_ymd. //! //! short_date: 13 Jun 2012 //! year: 2012 //! short_month_name: Jun //! month_name: June //! month_day: 13 // time12: 3:55 PM // time24/time: 15:55 //! //! //! iso_short: 20120513T15:55:47 //! time_xshort: 120513 15:55:47 //! time_short: 20120513 15:55:47 //! ext_time_short: Sun, 13 May 2012 15:55:47 EDT //! ymd_short: 20120513 //! week_short: 2012w19 //! month_short: 201205 //! tod_short: 155547 //! ymd_xshort: 120513 //! iso_week_short: 201219 //! commonlog: 13/May/2012:15:55:47 -0400 //! iso_ymd: 2012-05-13 (May) -W19-7 (Sun) //! ext_ymd: Sunday, 13 May 2012 //! ymd: 2012-05-13 //! smtp: Sun, 13 May 2012 15:55:47 -0400 //! nicez: 13 May 15:55:47 EDT //! nice: 13 May 15:55:47 //! month: 2012-05 //! week: 2012-w19 //! iso_week: 2012-W19 //! todz: 15:55:47 EDT //! tod: 15:55:47 //! http: Sun, 13 May 2012 19:55:47 GMT //! ctime: Sun May 13 15:55:47 2012 //! //! xtime: 2012-05-13 15:55:47.000000 //! mtime: 2012-05-13 15:55 //! time: 2012-05-13 15:55:47 //! ext_time: Sunday, 13 May 2012 15:55:47 //! iso_time: 2012-05-13 (May) -W19-7 (Sun) 15:55:47 UTC-4 //! todz_iso: 15:55:47 UTC-4 //! mod: 15:55 //! xtod: 15:55:47.000000 string simple_macro_format_date(Fins.Template.TemplateData data, mapping|void arguments) { string res; if(arguments->var) { mixed p = arguments->var; if(intp(p)) p = Calendar.Second(p); if(!p) return ""; if(! arguments->format) arguments->format="ext_ymd"; switch(arguments->format) { case "time24": case "time": res = sprintf("%02d:%02d", p->hour_no(), p->minute_no()); break; case "time12": int v; res = sprintf("%2d:%02d %s", (v=(p->hour_no()%12))?v:12, p->minute_no(), ((p->hour_no()/12)?"PM":"AM")); break; case "short_month_name": res = p->month_shortname(); break; case "month_name": res = p->month_name(); break; case "month_day": res = (string)p->month_day(); break; case "year": res = (string)p->year_no(); break; case "short_date": res = format_short_date(p); break; default: res = p["format_" + arguments->format](); } if(arguments->store) { mixed d = data->get_data(); d[arguments->store] = res; return ""; } else return res; } } protected string format_short_date(object d) { write("d: %O\n", d); return d->month_day() + " " + d->month_shortname() + " " + d->year_no(); } //! args: size string simple_macro_friendly_size(Fins.Template.TemplateData data, mapping|void args) { if(args->size) { int size = (int)args->size; if(size < 1024) return size + " bytes"; if(size < 1024*1024) return sprintf("%.1f KB", size/1024.0); if(size < 1024*1024*1024) return sprintf("%.2f MB", size/(1024.0*1024.0)); if(size < 1024*1024*1024*1024) return sprintf("%.12f GB", size/(1024.0*1024.0*1024.0)); } else return "--"; } //! get an rss feed and store the items in a variable. string simple_macro_rss_feed(Fins.Template.TemplateData data, mapping|void args) { mixed res; int max = 5; if(args->max) max = (int) args->max; if(!args->store) return "rss_feed macro requires a variable to store in."; if(!args->url) return "rss_feed macro requires a url to fetch."; #if constant(Public.Parser.XML2) && constant(Public.Syndication) res = rss_fetch(args->url, max); #else res = ({(["title": "RSS Feeds Unavailable - Install Public.Parser.XML2"])}); #endif logger->info("Storing %O", res); mixed d = data->get_data(); d[args->store] = res; return ""; return ""; } //! get an image rss feed (smugmug, flicker, etc) string simple_macro_image_feed(Fins.Template.TemplateData data, mapping|void args) { mixed res; int max = 5; if(args->max) max = (int) args->max; if(!args->store) return "image_feed macro requires a variable to store in."; if(!args->url) return "image_feed macro requires a url to fetch."; #if constant(Public.Parser.XML2) && constant(Public.Syndication) res = image_fetch(args->url, max); #else res = ({(["title": "Image/RSS Feeds Unavailable - Install Public.Parser.XML2"])}); #endif //werror("res: %O\n", res); mixed d = data->get_data(); d[args->store] = res; return ""; return ""; } mixed image_fetch(string rssurl, int max, int|void timeout) { object r = rss_fetch(rssurl, max, timeout); array x = ({}); mapping res = ([]); if(r && r->items) { foreach(r->items;; object item) { mapping image = ([]); mapping md = item->data["http://search.yahoo.com/mrss/"]; if(md && md->thumbnail) { image = md["thumbnail"]->get_attributes(); image->thumbnail = image->url; image->title = item->data->title; image->description = item->data->description; image->date = Calendar.dwim_time(item->data->pubDate); image->url = item->data->link; x+=({image}); if(sizeof(x) >= max) break; } else continue; } res->photos=x; res->title = r->data->title; res->url = r->data->link; } return res; } #if constant(Public.Parser.XML2) && constant(Public.Syndication) mixed rss_fetch(string rssurl, int max, int|void timeout) { string rss; object r; if(!(rss = feed_data[rssurl])) { logger->info("rss-reader getting " + rssurl); if(has_prefix(rssurl, "file://")) rss = Stdio.read_file(rssurl[7..]); if(has_prefix(rssurl, "action://")) { Fins.InternalRequest req = Fins.InternalRequest("GET", rssurl[9..], ([])); mixed res = app->handle_request(req); werror("res: %O\n", res); if(!res || res->error != 200) return 0; if(res->type != "text/xml") return 0; else rss = res->data; } else rss = Protocols.HTTP.get_url_data(rssurl); if(rss) feed_data[rssurl] = rss; } mixed e = catch { if(rss) { logger->info("rss-reader parsing " + rssurl); r = Public.Syndication.RSS.parse(rss); } }; if(e) logger->exception("Error parsing RSS Feed.", e); return r; } #else mixed rss_fetch(string rssurl, int max, int|void timeout) { return ([]); } #endif