package javango.contrib.jquery;
import static javango.contrib.freemarker.Helper.renderToResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javango.contrib.hibernate.HibernateUtil;
import javango.contrib.hibernate.ModelForm;
import javango.contrib.jquery.widgets.JqueryLookupWidget;
import javango.core.User;
import javango.db.Manager;
import javango.db.Managers;
import javango.db.QuerySet;
import javango.db.QuerySetPage;
import javango.db.QuerySetPaginator;
import javango.forms.Forms;
import javango.forms.fields.CharField;
import javango.forms.fields.Field;
import javango.forms.fields.FieldFactory;
import javango.http.HttpRequest;
import javango.http.HttpResponse;
import javango.http.SimpleHttpResponse;
import javango.util.StringFormat;
import org.apache.commons.logging.LogFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.mapping.PersistentClass;
import com.google.inject.Inject;
public class JqueryViews {
HibernateUtil hibernateUtil;
Managers managers;
Forms forms;
FieldFactory fieldFactory;
@Inject
public JqueryViews(HibernateUtil hibernateUtil, Managers managers, Forms forms, FieldFactory fields) {
super();
this.hibernateUtil = hibernateUtil;
this.managers = managers;
this.forms = forms;
this.fieldFactory = fields;
}
protected String getSearchProperty(User u, PersistentClass pc) { // throws NotAuthorizedException {
Class<?> modelClass = pc.getMappedClass();
Promptable p = (Promptable)modelClass.getAnnotation(Promptable.class);
if (p == null) return null;
if ("".equals(p.requiredRole())) { // no authority required
return p.property();
}
if (u == null) {
// throw new NotAuthorizedException();
return null;
}
if ("*".equals(p.requiredRole())) { // any authorized user
return p.property();
}
if (u.hasRole(p.requiredRole())) { // user has the role
return p.property();
}
// throw new NotAuthorizedException();
return null;
}
public HttpResponse lookup(HttpRequest request, String model) throws Exception {
Configuration cfg = hibernateUtil.getConfiguration();
PersistentClass pc = cfg.getClassMapping(model);
String queryString = request.getParameter("term");
String searchProperty = getSearchProperty(request.getUser(), pc);
if (searchProperty == null) {
return new SimpleHttpResponse("Unsupported class " + model + "|").setMimeType("text/plain");
}
Manager<Object> manager = managers.forClass(pc.getMappedClass());
QuerySet<Object> qs = manager.filter(searchProperty + "__ilike", queryString + "%" )
.limit(0,500);
StringBuilder b = new StringBuilder("[");
char comma = ' ';
for (Object o : qs) {
b.append(String.format("%s{ \"id\": \"%s\", \"label\": \"%s\", \"value\": \"%s\"}", comma, manager.getPk(o), o.toString(), o.toString()));
comma = ',';
}
b.append("]");
return new SimpleHttpResponse(b.toString()).setMimeType("application/json");
}
public HttpResponse promptForm(HttpRequest request) throws Exception {
return doPrompt(request, false);
}
public HttpResponse prompt(HttpRequest request) throws Exception {
return doPrompt(request, true);
}
public String asQueryString(Map<String, String[]> map) {
StringBuilder s = new StringBuilder();
try {
for (Entry<String, String[]> e : map.entrySet()) {
s.append(String.format("%s=%s&", e.getKey(), URLEncoder.encode(e.getValue()[0], "utf-8")));
}
return s.toString();
} catch (UnsupportedEncodingException e) {
LogFactory.getLog(JqueryViews.class).error(e,e);
}
return s.toString();
}
public HttpResponse doPrompt(HttpRequest request, boolean search) throws Exception {
String encodedString = request.getParameter("prompt_data");
Map<String, Object> context = new HashMap<String, Object>();
Map<String, String> params = JqueryLookupWidget.decrypt(encodedString);
Configuration cfg = hibernateUtil.getConfiguration();
PersistentClass pc = cfg.getClassMapping(params.get("model"));
ModelForm form = forms.newForm(ModelForm.class);
form.setPrefix("jquery_lookup_form"); // make sure field names don't conflict with something else in the form.
Class<?> clazz = pc.getMappedClass();
form.setModel(clazz);
String fields = params.get("search");
String fieldName = params.get("field");
String display = params.get("display");
String results = params.get("results"); // URLDecoder.decode(params.get("results"), "UTF-16");
String orderBy = params.get("order_by");
String[] resultColumns = results.split(",");
context.put("StringFormat", new StringFormat()); // needed in the template to format the display
context.put("field_name", fieldName);
context.put("field_list", resultColumns);
context.put("display", display);
context.put("prompt_data", URLEncoder.encode(encodedString, "UTF-8"));
// fields may have joins or header information, parse
List<String> modelFields = new ArrayList<String>(); // list of fields that come directly from the model
Map<String,Field<String>> addFormFields = new HashMap<String, Field<String>>(); // map of form fields to add to the form
for(String field : fields.split(",")) {
field = field.trim();
String[] parts = field.split(":");
if (parts.length == 1) {
modelFields.add(field);
} else { // this field has title information included.
// Verbose Name:field__join__searchtype
// Yes this means that in order for joins to be used the field must have a custom name TODO
// parts[0] = verboseName
// parts[1] = field
Field<String> formField = fieldFactory.newField(CharField.class)
.setAllowNull(true)
.setRequired(false)
.setName(parts[1])
.setVerboseName(parts[0]);
addFormFields.put(parts[1],formField);
}
}
if (!modelFields.isEmpty()) {
form.setInclude(modelFields.toArray(new String[]{}));
} else {
form.setInclude("");
}
form.getFields().putAll(addFormFields);
for(Field f : form.getFields().values()) {
f.setRequired(false).setAllowNull(true);
}
if (search) {
context.put("form_url", String.format("%s/%s", request.getContext(), request.getPath()));
Map<String, String[]> searchParams = new HashMap<String, String[]>(request.getParameterMap());
if (searchParams.containsKey("page")) searchParams.remove("page");
if (searchParams.containsKey("prompt_data")) searchParams.remove("prompt_data");
form.bind(request.getParameterMap());
if (form.isValid()) {
int page;
try {
page = new Integer(request.getParameter("page"));
} catch (NumberFormatException e) {
page = 1;
}
Manager<?> manager = managers.forClass(clazz);
QuerySet<?> qs = manager.filter(form.getCleanedData());
if (orderBy != null) {
qs = qs.orderBy(orderBy);
} else if (resultColumns != null && resultColumns.length > 0) {
qs = qs.orderBy(resultColumns);
}
QuerySetPaginator<?> paginator = new QuerySetPaginator(qs,15);
QuerySetPage<?> objects = paginator.getPage(page);
context.put("objects", objects);
context.put("query_string", asQueryString(searchParams));
context.put("form", form);
return renderToResponse("javango/contrib/jquery/prompt.ftl", context);
}
} else {
context.put("form_url", String.format("%s/%ssearch/", request.getContext(), request.getPath()));
// context.put("form_url", String.format("./%ssearch/", request.getPath()));
}
context.put("form", form);
return renderToResponse("javango/contrib/jquery/prompt.ftl", context);
}
}