package javango.contrib.admin.views;
import static javango.contrib.freemarker.Helper.renderToResponse;
import static javango.util.Humanize.humanize;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javango.api.Settings;
import javango.contrib.admin.FieldSet;
import javango.contrib.admin.MetaOptions;
import javango.contrib.admin.api.AdminAction;
import javango.contrib.admin.api.AdminOptions;
import javango.contrib.admin.api.ModelAdmin;
import javango.contrib.hibernate.HibernateUtil;
import javango.contrib.hibernate.ModelForm;
import javango.contrib.jquery.Helper;
import javango.db.Manager;
import javango.db.ManagerException;
import javango.db.Managers;
import javango.db.PaginatorWidget;
import javango.db.QuerySet;
import javango.db.QuerySetPage;
import javango.db.QuerySetPaginator;
import javango.forms.Form;
import javango.forms.fields.CharField;
import javango.forms.fields.DateField;
import javango.forms.fields.Field;
import javango.forms.fields.FieldFactory;
import javango.forms.fields.annotations.FieldProperties;
import javango.http.Http404;
import javango.http.Http500;
import javango.http.HttpException;
import javango.http.HttpRequest;
import javango.http.HttpResponse;
import javango.http.HttpResponseRedirect;
import javango.http.SimpleHttpResponse;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Provider;
public class Main {
private final static Log log = LogFactory.getLog(Main.class);
protected Injector injector;
protected HibernateUtil hibernateUtil;
protected FieldFactory fieldFactory;
protected Provider<Helper> jqueryProvider;
protected AdminOptions options;
protected Managers managers;
protected Settings settings;
@Inject
public Main(Injector injector, Settings settings, HibernateUtil hibernateUtil, FieldFactory fieldFactory, Provider<Helper> jqueryProvider, AdminOptions options, Managers managers) {
super();
this.hibernateUtil = hibernateUtil;
this.fieldFactory = fieldFactory;
this.jqueryProvider = jqueryProvider;
this.options = options;
this.managers = managers;
this.settings = settings;
this.injector = injector;
}
private String findVerboseName(Class clazz, ModelAdmin ma) {
if (ma.getVerboseName() != null) {
return ma.getVerboseName();
}
String className = clazz.getName();
MetaOptions options = (MetaOptions)clazz.getAnnotation(MetaOptions.class);
return options == null || "".equals(options.verboseName())
? className.substring(className.lastIndexOf(".")+1)
: options.verboseName();
}
private Manager findManager(Class clazz, ModelAdmin ma) {
Manager m = ma.getManager();
if (m == null) {
m = managers.forClass(clazz);
}
return m;
}
protected void updateContext(Map<String, Object> context , Class pc, ModelAdmin ma) {
context.put("entity", new ModelDefinition(pc, ma));
context.put("app_name", options.getAppName());
}
public class ModelDefinition {
protected String className;
protected String verboseName;
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getVerboseName() {
return verboseName;
}
public void setVerboseName(String verboseName) {
this.verboseName = verboseName;
}
public ModelDefinition(Class clazz, ModelAdmin ma) {
super();
this.className = clazz.getName();
this.verboseName = findVerboseName(clazz, ma);
}
@Override
public String toString() {
return verboseName;
}
}
public HttpResponse index(HttpRequest request) {
Map<String, List<ModelDefinition>> packageMap = new HashMap<String, List<ModelDefinition>>();
Iterator<Class> i = options.getClassMappings();
while (i.hasNext()) {
Class pc = i.next();
ModelAdmin ma = options.getModelAdmin(pc);
if (ma.isAuthorized(request.getUser())) {
String className = pc.getName();
String packageName = className.substring(0,className.lastIndexOf("."));
if (log.isDebugEnabled()) log.debug ("Adding class: " + className);
if (packageMap.containsKey(packageName)) {
List<ModelDefinition> appList = packageMap.get(packageName);
appList.add(new ModelDefinition(pc, ma));
} else {
List<ModelDefinition> appList = new ArrayList<ModelDefinition>();
appList.add(new ModelDefinition(pc, ma));
packageMap.put(packageName, appList);
}
} else if (log.isDebugEnabled()){
log.debug("Skipping " + pc.getName() + " due to authority.");
}
}
log.debug("Complete parsing class mappings");
Map<String, Object> context = new HashMap<String, Object>();
context.put("package_map", packageMap);
context.put("page_title", "Site administration");
context.put("app_name", options.getAppName());
return renderToResponse(options.getIndexTemplate(), context);
}
protected int getPageNumber(HttpRequest request) {
String value = request.getParameter("p");
try {
return value == null ? 1 : Integer.valueOf(value);
} catch (NumberFormatException e) {
log.debug(e,e);
return 1;
}
}
protected String generateSearchParams(HttpRequest request, Map<String, String> map) {
StringBuilder b = new StringBuilder();
for (Map.Entry<String, String[]> entry : request.getParameterMap().entrySet()) {
if (entry.getKey().contains("__")) {
String[] value = entry.getValue();
if (value != null && value.length > 0 & StringUtils.isNotEmpty(value[0])) {
// TODO Add AND ability, chain the String[] somehow...
String valueString = entry.getValue()[0];
if (map != null)
try {
map.put(entry.getKey(), valueString);
b.append(String.format("%s=%s&", entry.getKey(), URLEncoder.encode(valueString, settings.getDefaultCharset())));
} catch (UnsupportedEncodingException e) {
log.error(e,e);
}
}
}
}
return b.toString();
}
public HttpResponse search(HttpRequest request, String model) throws HttpException {
Class pc = options.getClassMapping(model);
if (pc == null) {
throw new Http404("Class not found");
}
ModelAdmin ma = options.getModelAdmin(pc);
if (!ma.isReader(request.getUser())) {
throw new Http404("Class not found, or not authorized");
}
String[] property_list = ma.getListSearchFields();
Form form = ma.getSearchForm();
if (form == null) {
String[] includeFields = property_list == null ? getIncludeFields(pc) : property_list;
ModelForm<?> mForm = new ModelForm(fieldFactory, hibernateUtil, managers);
mForm.setInclude(includeFields);
mForm.setModel(pc);
for (Field f : mForm.getFields().values()) {
f.setRequired(false).setAllowNull(true);
f.setEditable(true);
if (f.getClass().equals(CharField.class)) {
f.setName(f.getName() + "__ilike");
} else if (f.getClass().equals(DateField.class)) {
f.setName(f.getName() + "__date");
} else {
f.setName(f.getName() + "__eq");
}
}
form = mForm;
}
if ("POST".equals(request.getMethod())) {
form.bind(request.getParameterMap());
if (form.isValid()) {
// TODO better way to get the relative 'admin' part of the url
Map<String, String> params = new HashMap<String, String>();
generateSearchParams(request, params);
return new HttpResponseRedirect("../", params);
}
}
Map<String, Object> context = new HashMap<String, Object>();
context.put("form", form);
context.put("jquery", jqueryProvider.get().setBasePath(request.getContext()).forForm(form));
updateContext(context, pc, ma);
context.put("page_title", String.format("%s Search", findVerboseName(pc, ma)));
return renderToResponse(ma.getSearchFormTemplate(), context);
}
public HttpResponse changeList(HttpRequest request, String model) throws HttpException {
try {
Class pc = options.getClassMapping(model);
if (pc == null) {
throw new Http404("Class not found");
}
ModelAdmin ma = options.getModelAdmin(pc);
if (!ma.isReader(request.getUser())) {
throw new Http404("Class not found, or not authorized");
}
Manager<?> manager = findManager(pc, ma);
String[] property_list = ma.getListDisplay();
List<String> header_list = new ArrayList<String>();
for(String property : property_list) {
header_list.add(getChangeListLabel(pc, ma, property));
}
Map<String, String> searchParams = new HashMap<String, String>();
for(Entry<String, String[]> e : request.getParameterMap().entrySet()) {
if (e.getKey().contains("__")) {
String value = e.getValue() == null ? null : e.getValue().length == 0 ? null : e.getValue()[0];
searchParams.put(e.getKey(), value);
}
}
QuerySet<?> qs = manager.filter(searchParams);
String[] orderBy = ma.getOrderBy();
if (orderBy == null) {
qs = qs.orderBy(manager.getPkProperty());
} else {
qs = qs.orderBy();
}
String tool = request.getParameter("tool");
// an action has been selected or clicked
if (tool != null) {
for (AdminAction action : ma.getListTools()) {
if (tool.equals(action.getClass().getName())) {
return action.execute(ma, request, qs);
}
}
}
QuerySetPaginator<?> paginator = new QuerySetPaginator(qs, 30);
String query_string = generateSearchParams(request, searchParams);
QuerySetPage<?> page = paginator.getPage(getPageNumber(request));
List<?> object_list = page.getObjectList();
Map<String, Object> context = new HashMap<String, Object>();
context.put("header_list", header_list);
context.put("property_list", property_list);
context.put("object_list", object_list);
context.put("key_property", manager.getPkProperty());
context.put("link_property", ma.getListDisplayLinks() == null ? property_list[0] : ma.getListDisplayLinks());
context.put("paginator", new PaginatorWidget(paginator, getPageNumber(request), query_string));
context.put("query_string", query_string);
context.put("list_tools", ma.getListTools());
updateContext(context, pc, ma);
context.put("page_title", String.format("Select %s to change", findVerboseName(pc, ma)));
return renderToResponse(ma.getChangeListTemplate(), context);
} catch (ManagerException e) {
throw new HttpException(e);
}
}
/**
* Given a class and property return the Label for the change list.
* @param pc
* @param ma
* @param property
* @return
*/
private String getChangeListLabel(Class pc, ModelAdmin ma, String property) {
// todo maybe add ability to add labels to modeladmin and add that here.
FieldProperties properties = (FieldProperties)pc.getAnnotation(FieldProperties.class);
String label = null;
if (properties != null) {
label = properties.label();
}
if (label == null) {
String pieces[] = property.split(":");
if (pieces.length > 1) { // includes a :
label = pieces[0];
} else { // does not include the :
pieces = property.split("\\.");
property = pieces[pieces.length-1];
label = humanize(property);
}
}
return label;
}
public HttpResponse add(HttpRequest request, String model) throws HttpException {
return change(request, model, null);
}
protected String[] getIncludeFields(Class pc) {
FieldSet fieldSet = (FieldSet)pc.getAnnotation(FieldSet.class);
if (fieldSet != null) {
return fieldSet.fields();
}
return null;
}
public HttpResponse change(HttpRequest request, String model, String object_id) throws HttpException {
try {
object_id = object_id == null ? null : URLDecoder.decode(object_id, settings.getDefaultCharset());
// TODO Changet object_id to an Object and figure out the correct datatype from the dao. dont' think this is necessary
Class pc = options.getClassMapping(model);
if (pc == null) {
throw new Http404("Class not found");
}
ModelAdmin ma = options.getModelAdmin(pc);
if (object_id != null && !ma.isEditor(request.getUser())) {
throw new Http404("Class not found, or not authorized");
} else if (!ma.isAuthor(request.getUser())) {
throw new Http404("Class not found, or not authorized");
}
Manager manager = findManager(pc, ma);
Object object = null;
if (object_id != null) {
object = manager.get(object_id);
if (object == null) {
return new SimpleHttpResponse("Object not found");
}
}
String[] property_list = ma.getFields();
Form form = ma.getForm();
if (form == null) {
String[] includeFields = property_list == null ? getIncludeFields(pc) : property_list;
ModelForm mForm = new ModelForm(fieldFactory, hibernateUtil, managers);
if (includeFields != null) mForm.setInclude(includeFields);
mForm.setModel(pc);
form = mForm;
}
if ("POST".equals(request.getMethod())) {
// update the object
if (object == null) object=injector.getInstance(pc);
form.bind(request.getParameterMap());
// form = modelFactory.form(pc.getMappedClass(), request.getParameterMap());
if (form.isValid()) {
// if (object_id == null) { // this is a new object
// if (!bl.canCreate()) {
// throw new HttpException("Unable to create");
// }
// } else {
// if (!bl.canUpdate(object)) {
// throw new HttpException("Unable to updte");
// }
// }
form.clean(object);
try {
if (object_id == null) {
manager.create(object);
} else {
manager.save(object);
}
if (request.getParameterMap().containsKey("_addanother")) {
return new HttpResponseRedirect("../add");
} else if (request.getParameterMap().containsKey("_continue")) {
return new HttpResponseRedirect(String.format("../%s/", manager.getPk(object)));
}
return new HttpResponseRedirect("..");
} catch (ManagerException e) {
request.getSession().addError(e.getMessage());
}
}
} else {
// display the form
if (object != null) form.setInitial(object);
}
Map<String, Object> context = new HashMap<String, Object>();
context.put("property_list", property_list);
context.put("object", object);
context.put("form", form);
context.put("object_id", object_id);
context.put("jquery", jqueryProvider.get().setBasePath(request.getContext()).forForm(form));
updateContext(context, pc, ma);
context.put("page_title", String.format("Change %s", findVerboseName(pc, ma)));
return renderToResponse(ma.getChangeFormTemplate(), context);
} catch (Exception e) {
log.error(e,e);
throw new HttpException(e);
}
}
public HttpResponse delete(HttpRequest request, String model, String object_id) throws HttpException {
try {
object_id = object_id == null ? null : URLDecoder.decode(object_id, settings.getDefaultCharset());
Class pc = options.getClassMapping(model);
if (pc == null) {
throw new Http404("Class not found");
}
ModelAdmin ma = options.getModelAdmin(pc);
Manager manager = findManager(pc, ma);
Object object = manager.get(object_id);
if (object == null) {
throw new Http404("Not found"); // TODO 404
}
// if (!bl.canDelete(object)) {
// throw new HttpException("Unauthorized to delete");
// }
if ("POST".equals(request.getMethod())) {
try {
manager.delete(object);
return new HttpResponseRedirect("../..");
} catch (ManagerException e) {
request.getSession().addError(e.getMessage());
}
}
Map<String, Object> context = new HashMap<String, Object>();
context.put("object", object);
updateContext(context, pc, ma);
context.put("page_title", "Are you sure?");
return renderToResponse(ma.getConfirmDeleteTemplate(), context);
} catch (Exception e) {
log.error(e,e);
throw new HttpException(e);
}
}
public HttpResponse resetdb(HttpRequest request) {
if ("POST".equals(request.getMethod())) {
Configuration cfg = hibernateUtil.getConfiguration();
new SchemaExport(cfg).drop(false, true);
new SchemaExport(cfg).create(false, true);
request.getSession().addMessage("Tables created successfully");
return new HttpResponseRedirect("..");
}
Map<String, Object> context = new HashMap<String, Object>();
context.put("page_title", "Are you sure?");
return renderToResponse("/javango/contrib/admin/templates/confirm_reset_db.ftl", context);
}
}