diff src/nabble/view/web/template/NabbleNamespace.java @ 0:7ecd1a4ef557

add content
author Franklin Schmidt <fschmidt@gmail.com>
date Thu, 21 Mar 2019 19:15:52 -0600
parents
children 18cf4872fd7f
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/nabble/view/web/template/NabbleNamespace.java	Thu Mar 21 19:15:52 2019 -0600
@@ -0,0 +1,1368 @@
+package nabble.view.web.template;
+
+import fschmidt.db.DbDatabase;
+import fschmidt.html.Html;
+import fschmidt.util.java.HtmlUtils;
+import fschmidt.util.mail.AlternativeMultipartContent;
+import fschmidt.util.mail.Content;
+import fschmidt.util.mail.HtmlTextContent;
+import fschmidt.util.mail.Mail;
+import fschmidt.util.mail.MailAddress;
+import fschmidt.util.mail.MailHome;
+import fschmidt.util.mail.PlainTextContent;
+import nabble.model.Executors;
+import nabble.model.Init;
+import nabble.model.Lucene;
+import nabble.model.Message;
+import nabble.model.ModelException;
+import nabble.model.ModelHome;
+import nabble.model.Node;
+import nabble.model.Person;
+import nabble.model.Site;
+import nabble.model.SystemProperties;
+import nabble.model.User;
+import nabble.modules.ModuleManager;
+import nabble.naml.compiler.Command;
+import nabble.naml.compiler.CommandSpec;
+import nabble.naml.compiler.CompileException;
+import nabble.naml.compiler.Encoder;
+import nabble.naml.compiler.IPrintWriter;
+import nabble.naml.compiler.Interpreter;
+import nabble.naml.compiler.Macro;
+import nabble.naml.compiler.Meaning;
+import nabble.naml.compiler.Namespace;
+import nabble.naml.compiler.Program;
+import nabble.naml.compiler.ScopedInterpreter;
+import nabble.naml.compiler.Source;
+import nabble.naml.compiler.Template;
+import nabble.naml.compiler.TemplatePrintWriter;
+import nabble.naml.compiler.Usage;
+import nabble.naml.namespaces.BasicNamespace;
+import nabble.naml.namespaces.StringList;
+import nabble.naml.namespaces.TemplateException;
+import nabble.view.lib.Jtp;
+import nabble.view.lib.Permissions;
+import nabble.view.lib.Shared;
+import nabble.view.lib.help.Help;
+import nabble.view.web.Javascript;
+import nabble.view.web.user.OnlineStatus;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.servlet.ServletException;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.regex.Pattern;
+
+
+@Namespace (
+	name = "nabble",
+	global = true
+)
+public final class NabbleNamespace {
+	private static final Logger logger = LoggerFactory.getLogger(NabbleNamespace.class);
+
+	public static final String newsflash = (String)Init.get("newsflash");
+
+	private static ThreadLocal<NabbleNamespace> tl = new ThreadLocal<NabbleNamespace>();
+
+	public static NabbleNamespace current() {
+		return tl.get();
+	}
+
+	private Site site;
+
+	public NabbleNamespace(Site site) {
+		if( site == null )
+			throw new NullPointerException("site is null");
+		this.site = site;
+		tl.set(this);
+	}
+
+	public Site site() {
+		return site;
+	}
+
+	public DbDatabase db() {
+		return site.getDb();
+	}
+
+	public static final CommandSpec root_node = CommandSpec.DO;
+
+	@Command public void root_node(IPrintWriter out,ScopedInterpreter<NodeNamespace> interp) {
+		NodeNamespace ns = new NodeNamespace(site().getRootNode());
+ 		out.print( interp.getArg(ns,"do") );
+	}
+
+	@Command public void site_id(IPrintWriter out,Interpreter interp) {
+		out.print( site.getId() );
+	}
+
+	@Command public void base_url(IPrintWriter out,Interpreter interp) {
+		out.print( interp.encode( site.getBaseUrl() ) );
+	}
+
+	public static final CommandSpec terms_of_use_url = CommandSpec.DO()
+		.optionalParameters("is_registration_form")
+		.build()
+	;
+
+	@Command public static void terms_of_use_url(IPrintWriter out,Interpreter interp) {
+		boolean isRegistrationForm = interp.getArgAsBoolean("is_registration_form", false);
+		out.print( interp.encode(Jtp.termsUrl(isRegistrationForm)));
+	}
+
+
+	public static final CommandSpec javascript_string_encode = CommandSpec.TEXT;
+
+	@Command public void javascript_string_encode(IPrintWriter out,Interpreter interp) {
+		out.print( HtmlUtils.javascriptStringEncode(interp.getArgString("text")) );
+	}
+
+	public static final CommandSpec break_up = CommandSpec.TEXT;
+
+	@Command public void break_up(IPrintWriter out,Interpreter interp) {
+		out.print( Jtp.breakUp(interp.getArgString("text")) );
+	}
+
+	public static final CommandSpec hide_emails = CommandSpec.TEXT;
+
+	@Command public void hide_emails(IPrintWriter out,Interpreter interp) {
+		Encoder encoder = interp.getEncoder();
+		interp.setEncoder(Encoder.TEXT);
+		String text = interp.getArgString("text");
+		interp.setEncoder(encoder);
+		out.print( interp.encode( ModelHome.hideAllEmails(text) ) );
+	}
+
+
+
+	@Namespace (
+		name = "help_instance",
+		global = false
+	)
+	public static class HelpNs {
+		private final Help help;
+
+		private HelpNs(Help help) {
+			this.help = help;
+		}
+
+		@Command public void url(IPrintWriter out,Interpreter interp) {
+			out.print( help.url() );
+		}
+
+		@Command public void link(IPrintWriter out,Interpreter interp) {
+			out.print( help.link() );
+		}
+	}
+
+	@Namespace (
+		name = "help",
+		global = false
+	)
+	public static class HelpsNs {
+
+		private static void print(IPrintWriter out,ScopedInterpreter<HelpNs> interp,Help help) {
+			out.print( interp.getArg(new HelpNs(help),"do") );
+		}
+
+		@Command public void index_path(IPrintWriter out,Interpreter interp) {
+			out.print( interp.encode( "/help/Index.jtp" ) );
+		}
+
+		public static final CommandSpec mailing_list_intro = CommandSpec.DO;
+
+		@Command public void mailing_list_intro(IPrintWriter out,ScopedInterpreter<HelpNs> interp) {
+			print(out,interp,Help.mailingListIntro);
+		}
+
+		public static final CommandSpec search = CommandSpec.DO;
+
+		@Command public void search(IPrintWriter out,ScopedInterpreter<HelpNs> interp) {
+			print(out,interp,Help.search);
+		}
+
+		public static final CommandSpec cataloging = CommandSpec.DO;
+
+		@Command public void cataloging(IPrintWriter out,ScopedInterpreter<HelpNs> interp) {
+			print(out,interp,Help.cataloging);
+		}
+
+		public static final CommandSpec embed_what_how = CommandSpec.DO;
+
+		@Command public void embed_what_how(IPrintWriter out,ScopedInterpreter<HelpNs> interp) {
+			print(out,interp,Help.embed_what_how);
+		}
+
+		public static final CommandSpec mixed_lengths = CommandSpec.DO;
+
+		@Command public void mixed_lengths(IPrintWriter out,ScopedInterpreter<HelpNs> interp) {
+			print(out,interp,Help.mixed_lengths);
+		}
+
+		public static final CommandSpec password = CommandSpec.DO;
+
+		@Command public void password(IPrintWriter out,ScopedInterpreter<HelpNs> interp) {
+			print(out,interp,Help.password);
+		}
+
+		public static final CommandSpec userid = CommandSpec.DO;
+
+		@Command public void userid(IPrintWriter out,ScopedInterpreter<HelpNs> interp) {
+			print(out,interp,Help.userid);
+		}
+	}
+	private static final HelpsNs helpsNs = new HelpsNs();
+
+	public static final CommandSpec help = CommandSpec.DO;
+
+	@Command public void help(IPrintWriter out,ScopedInterpreter<HelpsNs> interp) {
+		out.print( interp.getArg(helpsNs,"do") );
+	}
+
+
+
+
+
+	public static final CommandSpec truncate = new CommandSpec.Builder()
+		.dotParameter("text")
+		.parameters("size")
+		.optionalParameters("if_truncated")
+		.build()
+	;
+
+	@Command public void truncate(IPrintWriter out,Interpreter interp) {
+		String text = interp.getArgString("text");
+		String size = interp.getArgString("size");
+		int n;
+		try {
+			n = Integer.valueOf(size);
+		} catch (NumberFormatException e) {
+			throw new RuntimeException("Invalid \"size\" attribute: " + size);
+		}
+		if (text.length() > n) {
+			text = text.substring(0, n-4) + "...";
+			out.print(text);
+			Object ifTruncated = interp.getArg("if_truncated");
+			if (ifTruncated != null) {
+				out.print(ifTruncated);
+			}
+		} else
+			out.print(text);
+	}
+
+
+
+	private static final BasicNamespace.ExceptionNamespaceFactory<ErrorNamespace> myExceptionNamespaceFactory =
+		new BasicNamespace.ExceptionNamespaceFactory<ErrorNamespace>() {
+			public ErrorNamespace newExceptionNamespace(TemplateException ex) {
+				return new ErrorNamespace(ex);
+			}
+		}
+	;
+
+	public static final CommandSpec handle_exception = BasicNamespace.handle_exception;
+
+	@Command public void handle_exception(IPrintWriter out,ScopedInterpreter<ErrorNamespace> interp) {
+		interp.getFromStack(BasicNamespace.class).handleException(out,interp,myExceptionNamespaceFactory);
+	}
+
+
+	@Command public static void tabs_library_path(IPrintWriter out,Interpreter interp) {
+		out.print( interp.encode( Shared.getTabsPath() ) );
+	}
+
+	@Command public static void nabble_global_apps_url(IPrintWriter out,Interpreter interp) {
+		out.print( interp.encode( Jtp.homeContextUrl() + "/UserSites.jtp" ) );
+	}
+
+	@Command public void get_newsflash(IPrintWriter out,Interpreter interp) {
+		out.print( newsflash );
+	}
+
+
+	/** Number of pages to be displayed before/after the current page*/
+	private static final int NEIGHBOR_PAGES = 3;
+
+	public static final CommandSpec paging = new CommandSpec.Builder()
+		.parameters("total_rows","current_row","rows_per_page")
+		.scopedParameters("do")
+		.dotParameter("do")
+		.build()
+	;
+
+	@Command public void paging(IPrintWriter out,ScopedInterpreter<PagingNamespace> interp) {
+		int totalRows = interp.getArgAsInt("total_rows");
+		int currentRow = interp.getArgAsInt("current_row",0);
+		int rowsPerPage = interp.getArgAsInt("rows_per_page");
+		out.print( interp.getArg( new PagingNamespace(totalRows,currentRow,rowsPerPage,NEIGHBOR_PAGES), "do" ) );
+	}
+
+
+
+	// Basic Nabble Urls
+
+	@Command public void nabble_homepage(IPrintWriter out,Interpreter interp) {
+		out.print(Jtp.homePage());
+	}
+
+	public static final CommandSpec get_int = new CommandSpec.Builder()
+		.parameters("exception")
+		.optionalParameters("default")
+		.dotParameter("int")
+		.build()
+	;
+
+	@Command public void get_int(IPrintWriter out,Interpreter interp)
+		throws TemplateException
+	{
+		String ex = interp.getArgString("exception");
+		String s = interp.getArgString("int");
+		if( s != null ) {
+			s = s.trim();
+			if( s.length() > 0 ) {
+				try {
+					out.print( Integer.parseInt(s) );
+					return;
+				} catch(NumberFormatException e) {
+					throw TemplateException.newInstance( ex );
+				}
+			}
+		}
+		String def = interp.getArgString("default");
+		if( def == null )
+			throw TemplateException.newInstance( ex );
+		out.print( Integer.parseInt(def) );
+	}
+
+	@Command public void site_activity(IPrintWriter out,Interpreter interp) {
+		out.print( site().getActivity() );
+	}
+
+	@Command public void site_has_delete_date(IPrintWriter out,Interpreter interp) {
+		out.print( site().getDeleteDate() != null );
+	}
+
+	public static final CommandSpec site_delete_date = CommandSpec.DO;
+
+	@Command public void site_delete_date(IPrintWriter out,ScopedInterpreter<DateNamespace> interp) {
+		out.print( interp.getArg( new DateNamespace(site().getDeleteDate()), "do" ) );
+	}
+
+	public static final CommandSpec new_email = CommandSpec.DO;
+
+	@Command public void new_email(IPrintWriter out,ScopedInterpreter<EmailNamespace> interp) {
+		out.print( interp.getArg( new EmailNamespace(), "do" ) );
+	}
+
+	public static final CommandSpec parse_email = new CommandSpec.Builder()
+		.dotParameter("text")
+		.build()
+	;
+
+	@Command public void parse_email(IPrintWriter out,Interpreter interp)
+		throws ModelException.EmailFormat
+	{
+		String email = null;
+		String text = interp.getArgString("text");
+		if( text != null ) {
+			text = text.trim();
+			if( text.length() > 0 ) {
+				int posOpen = text.indexOf('<');
+				int posClose = text.indexOf('>');
+				if (posOpen >= 0 && posClose > posOpen)
+					text = text.substring(posOpen+1, posClose).trim();
+				if (!new MailAddress(text).isValid())
+					throw new ModelException.EmailFormat(text);
+				email = text;
+			}
+		}
+		out.print(email);
+	}
+
+	public static final CommandSpec is_valid_node= CommandSpec.DO()
+		.parameters("node_id")
+		.build()
+	;
+
+	@Command public void is_valid_node(IPrintWriter out,ScopedInterpreter<NodeNamespace> interp) {
+		long nodeId = interp.getArgAsLong("node_id");
+		Node node = site.getNode(nodeId);
+		out.print(node != null);
+	}
+
+	public static final CommandSpec get_node_from_id = CommandSpec.DO()
+		.parameters("node_id")
+		.build()
+	;
+
+	@Command public void get_node_from_id(IPrintWriter out,ScopedInterpreter<NodeNamespace> interp) {
+		long nodeId = interp.getArgAsLong("node_id");
+		Node node = site.getNode(nodeId);
+		out.print( interp.getArg(new NodeNamespace(node),"do") );
+	}
+
+	public static final CommandSpec get_user_from_id = CommandSpec.DO()
+		.parameters("user_id")
+		.build();
+
+	@Command public void get_user_from_id(IPrintWriter out,ScopedInterpreter<UserNamespace> interp)
+		throws IOException, ServletException
+	{
+		String userId = interp.getArgString("user_id");
+		Person person = site().getPerson(userId);
+		out.print( interp.getArg(new UserNamespace(person),"do") );
+	}
+
+	public static final CommandSpec user_groups = CommandSpec.DO;
+
+	@Command public void user_groups(IPrintWriter out,ScopedInterpreter<GroupList> interp) {
+		List<String> groups = Permissions.getGroups(site());
+		Object block = interp.getArg(new GroupList(groups),"do");
+		out.print(block);
+	}
+
+	@Namespace (
+		name = "group_list",
+		global = true
+	)
+	public static final class GroupList extends StringList {
+
+		GroupList(List<String> groups) {
+			super(groups);
+		}
+
+		@Command public void current_group(IPrintWriter out,Interpreter interp) {
+			out.print(get());
+		}
+	}
+
+	public static final CommandSpec permissions = CommandSpec.DO()
+		.optionalParameters("values")
+		.build()
+	;
+
+	@Command public void permissions(IPrintWriter out,ScopedInterpreter<PermissionList> interp)
+		throws IOException, ServletException
+	{
+		List<String> list = new ArrayList<String>();
+		String values = interp.getArgString("values");
+		for( String s : values.split(",") ) {
+			list.add( s.trim() );
+		}
+		Object block = interp.getArg(new PermissionList(list),"do");
+		out.print(block);
+	}
+
+	@Namespace (
+		name = "permission_list",
+		global = true
+	)
+	public static final class PermissionList extends StringList {
+
+		PermissionList(List<String> list) {
+			super(list);
+		}
+
+		@Command public void current_permission(IPrintWriter out,Interpreter interp) {
+			out.print(get());
+		}
+	}
+
+
+	public static final CommandSpec get_node = CommandSpec.DO()
+		.parameters("node")
+		.build()
+	;
+
+	@Command public void get_node(IPrintWriter out,ScopedInterpreter<NodeNamespace> interp) {
+		NodeNamespace nodeNs = interp.getArgAsNamespace(NodeNamespace.class,"node");
+		if( nodeNs == null )
+			throw new RuntimeException("node is null");
+		out.print( interp.getArg(nodeNs,"do") );
+	}
+
+	public static final CommandSpec get_user = CommandSpec.DO()
+		.parameters("user")
+		.build()
+	;
+
+	@Command public void get_user(IPrintWriter out,ScopedInterpreter<UserNamespace> interp) {
+		UserNamespace userNs = interp.getArgAsNamespace(UserNamespace.class,"user");
+		if( userNs == null ) {
+			out.print( (String)null );
+			return;
+		}
+		out.print( interp.getArg(userNs,"do") );
+	}
+
+	public static final CommandSpec get_subscription = CommandSpec.DO()
+		.parameters("subscription")
+		.build()
+	;
+
+	@Command public void get_subscription(IPrintWriter out,ScopedInterpreter<SubscriptionNamespace> interp) {
+		SubscriptionNamespace subscriptionNs = interp.getArgAsNamespace(SubscriptionNamespace.class,"subscription");
+		out.print( interp.getArg(subscriptionNs,"do") );
+	}
+/*
+not needed yet
+	public static final CommandSpec get_mailing_list = CommandSpec.DO()
+		.parameters("mailing_list")
+		.build()
+	;
+
+	@Command public void get_mailing_list(IPrintWriter out,ScopedInterpreter<MailingListNamespace> interp) {
+		MailingListNamespace ns = interp.getArgAsNamespace(MailingListNamespace.class,"mailing_list");
+		out.print( interp.getArg(ns,"do") );
+	}
+*/
+
+
+	@Command public void anyone_group(IPrintWriter out,Interpreter interp) {
+		out.print( Permissions.ANYONE_GROUP );
+	}
+
+	@Command public void registered_group(IPrintWriter out,Interpreter interp) {
+		out.print( Permissions.REGISTERED_GROUP );
+	}
+
+	@Command public void authors_group(IPrintWriter out,Interpreter interp) {
+		out.print( Permissions.AUTHOR_GROUP );
+	}
+
+	@Command public void administrators_group(IPrintWriter out,Interpreter interp) {
+		out.print( Permissions.ADMINISTRATORS_GROUP );
+	}
+
+	@Command public void view_permission(IPrintWriter out,Interpreter interp) {
+		out.print( Permissions.VIEW_PERMISSION );
+	}
+
+	public static final CommandSpec set_default_permissions = CommandSpec.NO_OUTPUT()
+		.scopedParameters("do")
+		.dotParameter("do")
+		.parameters("version")
+		.build()
+	;
+
+	@Command public void set_default_permissions(IPrintWriter out,ScopedInterpreter<DefaultPermissionEditorNamespace> interp) {
+		String version = interp.getArgString("version");
+		synchronized( ("permission-lock-"+site().getId()).intern() ) {
+			site = site.getGoodCopy();
+			if( !Permissions.isPermissionVersion(site,interp.getArgString("version")) ) {
+				DefaultPermissionEditorNamespace ns;
+				while(true) {
+					db().beginTransaction();
+					try {
+						Site site = this.site.getGoodCopy();
+						Permissions.setPermissionVersion(site,version);
+						ns = new DefaultPermissionEditorNamespace(site);
+						interp.getArgString(ns,"do");
+						db().commitTransaction();
+					} finally {
+						db().endTransaction();
+					}
+					site = site.getGoodCopy();
+					if( ns.ok(site) )
+						break;
+					logger.error("set_default_permissions failed for "+site+", trying again");
+				}
+			}
+		}
+	}
+
+	@Namespace (
+		name = "default_permission_editor",
+		global = true
+	)
+	public static final class DefaultPermissionEditorNamespace {
+		private static class PermGroup {
+			final String perm;
+			final String group;
+
+			PermGroup(String perm,String group) {
+				this.perm = perm;
+				this.group = group;
+			}
+		}
+
+		private final Site site;
+		private final List<PermGroup> permissions = new ArrayList<PermGroup>();
+		private final List<PermGroup> sitePermissions = new ArrayList<PermGroup>();
+
+		DefaultPermissionEditorNamespace(Site site) {
+			this.site = site;
+		}
+
+		public static final CommandSpec add_permission = CommandSpec.NO_OUTPUT()
+			.parameters("group","permission")
+			.build()
+		;
+
+		@Command public void add_permission(IPrintWriter out,Interpreter interp) {
+			String perm = interp.getArgString("permission");
+			String group = interp.getArgString("group");
+			Permissions.addPermission(site,perm,group);
+			permissions.add(new PermGroup(perm,group));
+		}
+
+		public static final CommandSpec add_site_permission = add_permission;
+
+		@Command public void add_site_permission(IPrintWriter out,Interpreter interp) {
+			String perm = interp.getArgString("permission");
+			String group = interp.getArgString("group");
+			Permissions.addSiteDefaultPermission(site,perm,group);
+			sitePermissions.add(new PermGroup(perm,group));
+		}
+
+		boolean ok(Site site) {
+			for( PermGroup pg : permissions ) {
+				if( !Permissions.hasPermission(site,pg.group,pg.perm) ) {
+					logger.error("add_permission failed for "+site+" permission="+pg.perm+" group="+pg.group);
+					return false;
+				}
+			}
+			for( PermGroup pg : sitePermissions ) {
+				if( !Permissions.hasSiteDefaultPermission(site,pg.group,pg.perm) ) {
+					logger.error("add_site_permission failed for "+site+" permission="+pg.perm+" group="+pg.group);
+					return false;
+				}
+			}
+			return true;
+		}
+	}
+
+	public static final CommandSpec save_site_permissions = CommandSpec.DO;
+
+	@Command public void save_site_permissions(IPrintWriter out,ScopedInterpreter<SitePermissionEditorNamespace> interp) {
+		db().beginTransaction();
+		try {
+			interp.getArgString(new SitePermissionEditorNamespace(site()),"do");
+			db().commitTransaction();
+		} finally {
+			db().endTransaction();
+		}
+	}
+
+	@Namespace (
+		name = "site_permission_editor",
+		global = true
+	)
+	public static final class SitePermissionEditorNamespace {
+		private final Site site;
+
+		SitePermissionEditorNamespace(Site site) {
+			this.site = site;
+		}
+
+		public static final CommandSpec remove_site_permissions = CommandSpec.NO_OUTPUT;
+
+		@Command public void remove_site_permissions(IPrintWriter out,Interpreter interp) {
+			Permissions.removeSitePermissions(site);
+		}
+
+		public static final CommandSpec add_site_permission = CommandSpec.NO_OUTPUT()
+			.parameters("permission")
+			.optionalParameters("group")
+			.build()
+		;
+
+		@Command public void add_site_permission(IPrintWriter out,Interpreter interp) {
+			String perm = interp.getArgString("permission");
+			String group = interp.getArgString("group");
+			if( group == null )
+				Permissions.addSitePermission(site,perm);
+			else
+				Permissions.addSitePermission(site,perm,group);
+		}
+
+		public static final CommandSpec remove_site_permission = CommandSpec.NO_OUTPUT()
+			.parameters("permission")
+			.optionalParameters("group")
+			.build()
+		;
+
+		@Command public void remove_site_permission(IPrintWriter out,Interpreter interp) {
+			String group = interp.getArgString("group");
+			String perm = interp.getArgString("permission");
+			if (group == null)
+				Permissions.removeSitePermission(site,perm);
+			else
+				Permissions.removeSitePermission(site,perm,group);
+		}
+
+	}
+
+	public static final CommandSpec has_default_permission = new CommandSpec.Builder()
+		.parameters("group","permission")
+		.build()
+	;
+
+	@Command public void has_default_permission(IPrintWriter out,Interpreter interp) {
+		String group = interp.getArgString("group");
+		String perm = interp.getArgString("permission");
+		out.print( Permissions.hasPermission(site(),group,perm) );
+	}
+
+	public static final CommandSpec has_site_default_permission = has_default_permission;
+
+	@Command public void has_site_default_permission(IPrintWriter out,Interpreter interp) {
+		String group = interp.getArgString("group");
+		String perm = interp.getArgString("permission");
+		out.print( Permissions.hasSiteDefaultPermission(site(),group,perm) );
+	}
+
+	public static final CommandSpec group_has_site_permission = has_default_permission;
+
+	@Command public void group_has_site_permission(IPrintWriter out,Interpreter interp) {
+		String group = interp.getArgString("group");
+		String perm = interp.getArgString("permission");
+		out.print( Permissions.hasSitePermission(site(),group,perm) );
+	}
+
+	public static final CommandSpec site_has_site_permission = new CommandSpec.Builder()
+		.dotParameter("permission")
+		.build()
+	;
+
+	@Command public void site_has_site_permission(IPrintWriter out,Interpreter interp) {
+		String perm = interp.getArgString("permission");
+		out.print( Permissions.siteHasSitePermission(site(),perm) );
+	}
+
+	public static final CommandSpec remove_group = CommandSpec.NO_OUTPUT()
+		.parameters("group")
+		.build()
+	;
+
+	@Command public void remove_group(IPrintWriter out,Interpreter interp) {
+		String group = interp.getArgString("group");
+		Permissions.removeGroup(site(),group);
+	}
+
+	public static final CommandSpec users_in_group = CommandSpec.DO()
+		.parameters("group")
+		.build()
+	;
+
+	@Command public void users_in_group(IPrintWriter out,ScopedInterpreter<UserNamespace.UserList> interp) {
+		String group = interp.getArgString("group");
+		List<User> users = Permissions.getUsersInGroup(site,group);
+		UserNamespace.UserList usersNs = new UserNamespace.UserList(users);
+		out.print( interp.getArg(usersNs,"do") );
+	}
+
+	public static final CommandSpec site_users = new CommandSpec.Builder()
+		.parameters("length")
+		.optionalParameters("start", "filter")
+		.scopedParameters("do")
+		.dotParameter("do")
+		.outputtedParameters("do")
+		.build()
+	;
+
+	@Command public void site_users(IPrintWriter out,ScopedInterpreter<UserNamespace.UserList> interp) {
+		int start = interp.getArgAsInt("start",0);
+		int length = interp.getArgAsInt("length");
+		String cnd = interp.getArgString("filter");
+		List<User> users = site.getUsersByNodeCount(start,length,cnd);
+		UserNamespace.UserList usersNs = new UserNamespace.UserList(users);
+		out.print( interp.getArg(usersNs,"do") );
+	}
+
+	public static final CommandSpec site_user_count = CommandSpec.DO()
+		.optionalParameters("filter")
+		.build();
+
+	@Command public void site_user_count(IPrintWriter out,Interpreter interp) {
+		String cnd = interp.getArgString("filter");
+		out.print( site.getUserCount(cnd) );
+	}
+
+	@Command public void registered_filter(IPrintWriter out,Interpreter interp) {
+		out.print( "registered is not null" );
+	}
+
+	public static final CommandSpec online_users = CommandSpec.DO()
+		.optionalParameters("include_invisible_users")
+		.build();
+
+	@Command public void online_users(IPrintWriter out,ScopedInterpreter<UserNamespace.UserList> interp) {
+		boolean includeInvisibleUsers = interp.getArgAsBoolean("include_invisible_users", false);
+		List<User> users = OnlineStatus.getOnlineUsers(site, includeInvisibleUsers);
+		UserNamespace.UserList usersNs = new UserNamespace.UserList(users);
+		out.print( interp.getArg(usersNs,"do") );
+	}
+
+	@Command public void online_anonymous_users_count(IPrintWriter out,Interpreter interp) {
+		out.print( OnlineStatus.getOnlineAnonymousUsersCount(site) );
+	}
+
+	@Command public void online_invisible_users_count(IPrintWriter out,Interpreter interp) {
+		out.print( OnlineStatus.getOnlineInvisibleUsersCount(site) );
+	}
+
+	public static final CommandSpec url_belongs_to_site = new CommandSpec.Builder()
+		.parameters("url")
+		.build()
+	;
+
+	@Command public void url_belongs_to_site(IPrintWriter out,Interpreter interp)
+		throws TemplateException
+	{
+		try {
+			String url = interp.getArgString("url");
+			String domain = new URL(url).getHost();
+			Long siteId = Jtp.getSiteIdFromDomain(domain);
+			out.print( siteId != null && siteId.longValue() == site().getId() );
+		} catch(MalformedURLException e) {
+			throw new ModelException.InvalidPermalink();
+		}
+	}
+
+	public static final CommandSpec check_registered_user = new CommandSpec.Builder()
+		.parameters("email","password_hash")
+		.build()
+	;
+
+	@Command public void check_registered_user(IPrintWriter out,Interpreter interp) {
+		String email = interp.getArgString("email");
+		String pwd = interp.getArgString("password_hash");
+		User user = site.getUserFromEmail(email);
+		out.print( user != null && user.isRegistered() && user.checkPasscookie(pwd) );
+	}
+
+	public static final CommandSpec get_or_create_user = CommandSpec.DO()
+		.parameters("email")
+		.build()
+	;
+
+	@Command public void get_or_create_user(IPrintWriter out,ScopedInterpreter<UserNamespace> interp)
+		throws ModelException.EmailFormat
+	{
+		String email = interp.getArgString("email");
+		if (!new MailAddress(email).isValid())
+			throw new ModelException.EmailFormat(email);
+		User user = site.getOrCreateUser(email);
+		out.print( interp.getArg(new UserNamespace(user),"do") );
+	}
+
+	public static final CommandSpec exists_user_for_email = new CommandSpec.Builder()
+		.dotParameter("email")
+		.build()
+	;
+
+	@Command public void exists_user_for_email(IPrintWriter out,Interpreter interp)
+		throws ModelException.EmailFormat
+	{
+		String email = interp.getArgString("email");
+		User user = site.getUserFromEmail(email);
+		out.print( user != null );
+	}
+
+	public static final CommandSpec get_user_from_email = CommandSpec.DO()
+		.parameters("email")
+		.build()
+	;
+
+	@Command public void get_user_from_email(IPrintWriter out,ScopedInterpreter<UserNamespace> interp)
+		throws ModelException.EmailFormat
+	{
+		String email = interp.getArgString("email");
+		User user = site.getUserFromEmail(email);
+		if( user==null ) {
+			out.print( (String)null );
+			return;
+		}
+		out.print( interp.getArg(new UserNamespace(user),"do") );
+	}
+
+	public static final CommandSpec has_authenticated_user_with_name = new CommandSpec.Builder()
+		.parameters("name")
+		.build()
+	;
+
+	@Command public void has_authenticated_user_with_name(IPrintWriter out,Interpreter interp) {
+		String name = interp.getArgString("name");
+		out.print( site.getUserFromName(name) != null );
+	}
+
+	public static final CommandSpec get_authenticated_user_with_name = CommandSpec.DO()
+		.parameters("name")
+		.build()
+	;
+
+	@Command public void get_authenticated_user_with_name(IPrintWriter out,ScopedInterpreter<UserNamespace> interp)
+		throws ModelException.EmailFormat
+	{
+		String name = interp.getArgString("name");
+		User user = site.getUserFromName(name);
+		out.print( interp.getArg(new UserNamespace(user),"do") );
+	}
+
+	public static final CommandSpec banned_users = CommandSpec.DO;
+
+	@Command public void banned_users(IPrintWriter out,ScopedInterpreter<UserNamespace.UserList> interp) {
+		List<User> banned = Permissions.getBannedUsers(site());
+		Object block = interp.getArg(new UserNamespace.UserList(banned),"do");
+		out.print(block);
+	}
+
+	@Command public void administrator_notice(IPrintWriter out,Interpreter interp) {
+		out.print( SystemProperties.get("administrator.notice") );
+	}
+
+	@Command public void administrator_notice_version(IPrintWriter out,Interpreter interp) {
+		String version = SystemProperties.get("administrator.notice.version");
+		out.print( version == null? "0" : version );
+	}
+
+
+/*
+	public static final CommandSpec macro_text = new CommandSpec.Builder()
+		.parameters("macro")
+		.optionalParameters("namespace")
+		.build()
+	;
+
+	@Command public void macro_text(IPrintWriter out,Interpreter interp) {
+		String macro = interp.getArgString("macro");
+		String namespace = interp.getArgString("namespace");
+		out.print( site.getTemplates().source().getMacro(macro,namespace) );
+	}
+*/
+
+
+	public static final CommandSpec log = new CommandSpec.Builder()
+		.dotParameter("text")
+		.outputtedParameters()
+		.build()
+	;
+
+	@Command public void log(IPrintWriter out,Interpreter interp) {
+		interp.setEncoder(Encoder.TEXT);
+		String text = interp.getArgString("text");
+		NamlLogger.getLogger(site).log(text);
+	}
+
+	@Command public void get_log(IPrintWriter out,Interpreter interp) {
+		out.print( interp.encode( NamlLogger.getLogger(site).getLog() ) );
+	}
+
+	public static final CommandSpec clear_log = CommandSpec.NO_OUTPUT;
+
+	@Command public void clear_log(IPrintWriter out,Interpreter interp) {
+		NamlLogger.removeLogger(site);
+	}
+
+
+	@Command public void lucene_is_ready(IPrintWriter out,Interpreter interp) {
+		out.print( Lucene.isReady(site) );
+	}
+
+	@Command public void support_url(IPrintWriter out,Interpreter interp) {
+		out.print( Jtp.supportUrl() );
+	}
+
+	@Command public void dot_pack(IPrintWriter out,Interpreter interp) {
+	}
+
+
+
+
+
+	public static final CommandSpec call_later = new CommandSpec.Builder()
+		.dotParameter("param")
+		.optionalParameters("value")
+		.build()
+	;
+
+	private Map<String,Set<String>> callLaterMap = new LinkedHashMap<String,Set<String>>();
+
+	@Command public void call_later(IPrintWriter out,Interpreter interp) {
+		String param = interp.getArgString("param");
+		Set<String> values = callLaterMap.get(param);
+		if( values == null ) {
+			values = new LinkedHashSet<String>();
+			callLaterMap.put(param,values);
+		}
+		String value = interp.getArgString("value");
+		if( value == null )
+			value = "";
+		values.add(value);
+	}
+
+	private static final int JS_PATH_LIM = 1200;
+
+	@Command public void load_call_later_script(IPrintWriter out,Interpreter interp)
+		throws IOException, ServletException
+	{
+		if( !callLaterMap.isEmpty() ) {
+			String jsPath = "/template/NamlServlet.jtp?macro=js_page";
+			StringBuilder path = new StringBuilder();
+			path.append(jsPath);
+			for (Map.Entry<String, Set<String>> entry : callLaterMap.entrySet()) {
+				String param = entry.getKey();
+				Set<String> values = entry.getValue();
+				boolean isFirst = true;
+				for (String value : values) {
+					if (path.length() > JS_PATH_LIM) {
+						loadScript(out,path);
+						path.setLength(0);
+						path.append(jsPath);
+						isFirst = true;
+					}
+					if( isFirst ) {
+						path.append('&').append(param).append('=');
+						isFirst = false;
+					} else {
+						path.append('|');
+					}
+					path.append(HtmlUtils.urlEncode(HtmlUtils.urlEncode(value)));
+				}
+			}
+			loadScript(out,path);
+		}
+		callLaterMap = null;
+	}
+
+	private static void loadScript(IPrintWriter out,Object path) {
+		out.print( "<script type='text/javascript'>\n" );
+		out.print( "var scriptUrl = '" + path + "';\n" );
+		out.print( "scriptUrl += '&_=' + Math.floor(Math.random()*9999);\n" );
+		out.print( "$.getScript(scriptUrl, function() { Nabble.resizeFrames(); });\n" );
+		out.print( "</script>\n" );
+	}
+
+	public static final CommandSpec get_parameters_from_run_later = CommandSpec.DO()
+		.parameters("name")
+		.build()
+	;
+
+	@Command public void get_parameters_from_run_later(IPrintWriter out,ScopedInterpreter<RequestNamespace.ParameterValueList> interp) {
+		String name = interp.getArgString("name");
+		Set<String> values = callLaterMap.get(name);
+		List<String> list = values == null ? Collections.<String>emptyList() : new ArrayList<String>(values);
+		Object block = interp.getArg(new RequestNamespace.ParameterValueList(list),"do");
+		out.print(block);
+	}
+
+	// Tweaks & NAML code
+
+	public static final CommandSpec macro_source = new CommandSpec.Builder()
+		.parameters("id")
+		.optionalParameters("base", "breadcrumbs")
+		.scopedParameters("do")
+		.dotParameter("do")
+		.build()
+	;
+
+	@Command public void macro_source(IPrintWriter out,ScopedInterpreter<MacroSourceNamespace> interp) {
+		MacroSourceNamespace ns = new MacroSourceNamespace(site(),interp.getArgString("id"), interp.getArgString("base"), interp.getArgString("breadcrumbs"));
+		out.print( interp.getArg( ns, "do" ) );
+	}
+
+	public static final CommandSpec macro_editor = CommandSpec.DO()
+		.optionalParameters("id", "base", "breadcrumbs")
+		.build()
+	;
+
+	@Command public void macro_editor(IPrintWriter out,ScopedInterpreter<MacroEditorNamespace> interp) {
+		MacroEditorNamespace ns = new MacroEditorNamespace(site(),interp.getArgString("id"),interp.getArgString("base"), interp.getArgString("breadcrumbs"));
+		out.print( interp.getArg( ns, "do" ) );
+	}
+
+	public static final CommandSpec macro_search = CommandSpec.DO()
+		.parameters("query", "search_by")
+		.build();
+
+	@Command public void macro_search(IPrintWriter out,ScopedInterpreter<MacroSourceNamespace.Commands> interp)
+		throws IOException, ServletException, CompileException {
+		String query = interp.getArgString("query");
+		String searchBy = interp.getArgString("search_by");
+		boolean byName =  "name".equals(searchBy);
+		Map<String,MacroSourceNamespace.CommandInfo> map = new TreeMap<String, MacroSourceNamespace.CommandInfo>();
+		if (query != null) {
+			query = query.toLowerCase();
+			Pattern ptn = compileWildcardPattern(query);
+			Program program = site().getProgram();
+			List<Source> sources = program.getSources();
+			for (Source s : sources) {
+				for (Macro m : s.getMacros()) {
+					if (byName) {
+						boolean isOverridden = program.getMacroWhichOverrides(m) != null;
+						if (isOverridden)
+							continue;
+					}
+					boolean matches = byName? ptn.matcher(m.getName().toLowerCase()).matches() : ptn.matcher(m.element.toString().toLowerCase()).find();
+					if (matches) {
+						// Now we look for the first usage of this meaning
+						MacroSourceNamespace.CommandInfo info = getCommandInfo(m);
+						String key = m.getName() + '-' + MacroSourceNamespace.csv(m.getRequiredNamespaces());
+						map.put(key, info);
+					}
+				}
+			}
+		}
+		List<MacroSourceNamespace.CommandInfo> list = new ArrayList<MacroSourceNamespace.CommandInfo>(map.values());
+		Object block = interp.getArg(new MacroSourceNamespace.Commands(list),"do");
+		out.print(block);
+	}
+
+	private MacroSourceNamespace.CommandInfo getCommandInfo(Macro m) {
+		MacroSourceNamespace.CommandInfo info;
+		Set<Usage> usages = site.getProgram().getUsages(m);
+		if (usages == null || usages.size() == 0) {
+			info = new MacroSourceNamespace.CommandInfo(m, null, null);
+		} else {
+			Usage u = usages.iterator().next();
+			String usageBase = MacroSourceNamespace.asBaseParam(u.baseIds());
+			String breadcrumbs = MacroSourceNamespace.asBreadcrumbsParam(u.macroPath());
+			info = new MacroSourceNamespace.CommandInfo(m, usageBase, breadcrumbs);
+		}
+		return info;
+	}
+
+	private static Pattern compileWildcardPattern(String query) {
+		String[] a = query.split("\\*",-1);
+		StringBuilder regex = new StringBuilder();
+		boolean isFirst = true;
+		for( String s : a ) {
+			if( isFirst )
+				isFirst = false;
+			else
+				regex.append( ".*" );
+			if( s.length() > 0 )
+				regex.append( Pattern.quote(s) );
+		}
+		return Pattern.compile(regex.toString());
+	}
+
+	@Command public void has_custom_macros(IPrintWriter out,Interpreter interp)
+		throws CompileException
+	{
+		out.print(ModuleManager.getCustomMacros(site.getProgram()).size() > 0);
+	}
+
+	public static final CommandSpec custom_macros = CommandSpec.DO;
+
+	@Command public void custom_macros(IPrintWriter out,ScopedInterpreter<MacroSourceNamespace.Commands> interp)
+		throws IOException, ServletException, CompileException
+	{
+		Collection<Macro> macros = ModuleManager.getCustomMacros(site.getProgram());
+		printMacros(macros, out, interp);
+	}
+
+	private void printMacros(Collection<Macro> macros, IPrintWriter out, ScopedInterpreter<MacroSourceNamespace.Commands> interp) {
+		List<MacroSourceNamespace.CommandInfo> list = new ArrayList<MacroSourceNamespace.CommandInfo>();
+		Set<String> names = new HashSet<String>();
+		for (Macro m : macros) {
+			String key = m.getName() + '-' + MacroSourceNamespace.csv(m.getRequiredNamespaces());
+			if (names.contains(key))
+				continue;
+			names.add(key);
+			MacroSourceNamespace.CommandInfo info = getCommandInfo(m);
+			list.add(info);
+		}
+		Collections.sort(list, MacroSourceNamespace.CommandInfo.MACRO_NAME_COMPARATOR);
+		Object block = interp.getArg(new MacroSourceNamespace.Commands(list),"do");
+		out.print(block);
+	}
+
+	@Command public void has_configuration_macros(IPrintWriter out,Interpreter interp)
+		throws CompileException
+	{
+		out.print(ModuleManager.getConfigurationMacros(site.getProgram()).size() > 0);
+	}
+
+	public static final CommandSpec configuration_macros = CommandSpec.DO;
+
+	@Command public void configuration_macros(IPrintWriter out,ScopedInterpreter<MacroSourceNamespace.Commands> interp)
+		throws IOException, ServletException, CompileException
+	{
+		Collection<Macro> macros = ModuleManager.getConfigurationMacros(site.getProgram());
+		printMacros(macros, out, interp);
+	}
+
+	@Command public void has_tweak_exception(IPrintWriter out,Interpreter interp)
+		throws CompileException
+	{
+		out.print(site.getTweakException() != null);
+	}
+
+	@Command public void tweak_exception_message(IPrintWriter out,Interpreter interp)
+		throws CompileException
+	{
+		Exception e = site.getTweakException();
+		out.print(e == null? null : e.getMessage());
+	}
+
+	public static final CommandSpec advanced_editor_path = new CommandSpec.Builder()
+		.parameters("prev_url")
+		.build();
+
+	@Command public void advanced_editor_path(IPrintWriter out,Interpreter interp) {
+		String prev = interp.getArgString("prev_url");
+		out.print("/template/NamlEditor.jtp?prev=" + HtmlUtils.urlEncode(prev));
+	}
+
+	static boolean isCompiledAll(Program program) {
+		Meaning meaning = program.getMeaning(COMPILED_ALL_CHECK_ID);
+		return meaning != null && program.isCompiled(meaning);
+	}
+
+	private static final String COMPILED_ALL_CHECK_ID = "compiled_all_check!nabble:compile_all.naml";
+
+	@Command public void is_compiled_all(IPrintWriter out, Interpreter interp) {
+		Program program = interp.template().program();
+		out.print(isCompiledAll(program));
+	}
+
+	@Command public void run_compile_all(IPrintWriter out,Interpreter interp) throws CompileException {
+		CompileTest.compileAll(site.getProgram());
+	}
+
+	public static final CommandSpec naml_configuration = CommandSpec.DO;
+
+	@Command public void naml_configuration(IPrintWriter out,ScopedInterpreter<NamlConfigurationNamespace> interp)
+		throws IOException, ServletException, CompileException
+	{
+		out.print(interp.getArg(new NamlConfigurationNamespace(),"do"));
+	}
+
+	@Command public void js_basic_nabble_functions(IPrintWriter out,Interpreter interp)
+		throws CompileException
+	{
+		StringWriter sw = new StringWriter();
+		PrintWriter jsOut = new PrintWriter(sw);
+		Javascript.basicNabbleFunctions(jsOut);
+		jsOut.close();
+		out.print(sw.toString());
+	}
+
+	public static final CommandSpec NAME = new CommandSpec.Builder()
+		.dotParameter("name")
+		.build()
+	;
+
+	public static final CommandSpec has_site_property = NAME;
+
+	@Command public void has_site_property(IPrintWriter out,Interpreter interp) {
+		String name = interp.getArgString("name");
+		out.print(site.getProperty(name) != null);
+	}
+
+	public static final CommandSpec get_site_property = NAME;
+
+	@Command public void get_site_property(IPrintWriter out,Interpreter interp) {
+		String name = interp.getArgString("name");
+		out.print(site.getProperty(name));
+	}
+
+	public static final CommandSpec delete_site_property = CommandSpec.NO_OUTPUT()
+		.parameters("name")
+		.build()
+	;
+
+	@Command public void delete_site_property(IPrintWriter out,Interpreter interp) {
+		String name = interp.getArgString("name");
+		site.setProperty(name, null);
+		site.update();
+	}
+
+	public static final CommandSpec set_site_property = CommandSpec.NO_OUTPUT()
+		.parameters("name", "value")
+		.build()
+	;
+
+	@Command public void set_site_property(IPrintWriter out,Interpreter interp) {
+		String name = interp.getArgString("name");
+		String value = interp.getArgString("value");
+		site.setProperty(name, value);
+		site.update();
+	}
+
+
+	public static final CommandSpec to_html_list = CommandSpec.DO()
+		.parameters("text")
+		.build()
+	;
+
+	@Command public void to_html_list(IPrintWriter out,ScopedInterpreter<HtmlListNamespace> interp) {
+		String text = interp.getArgString("text");
+		HtmlListNamespace html = new HtmlListNamespace(new Html(text), null, Message.Format.HTML);
+		interp.getArgString(html,"do");  // processing, no output
+		out.print(html);
+	}
+
+
+	@Command public void is_embarrassing(IPrintWriter out,Interpreter interp) 	{
+		out.print(site.isEmbarrassing());
+	}
+
+	public static final Set<Long> sitesRunningBackup = Collections.synchronizedSet(new HashSet<Long>());
+
+	@Command public void is_running_backup(IPrintWriter out,Interpreter interp) {
+		out.print(sitesRunningBackup.contains(site.getId()));
+	}
+
+	public static final CommandSpec make_backup = CommandSpec.NO_OUTPUT()
+		.parameters("email")
+		.build()
+	;
+
+	@Command public void make_backup(IPrintWriter out,Interpreter interp) {
+		synchronized (sitesRunningBackup) {
+			if (sitesRunningBackup.contains(site.getId()))
+				return;
+			sitesRunningBackup.add(site.getId());
+			final String email = interp.getArgString("email");
+			Executors.executeSometime(new Runnable(){
+				public void run() {
+					File file = site.backup();
+					Template template = site.getTemplate( "backup email",
+						BasicNamespace.class, NabbleNamespace.class
+					);
+					Map<String,Object> params = new HashMap<String,Object>();
+					params.put("email", email);
+					params.put("file", file.getName());
+					template.run( TemplatePrintWriter.NULL, params,
+						new BasicNamespace(template),
+						new NabbleNamespace(site)
+					);
+					sitesRunningBackup.remove(site.getId());
+				}
+			});
+		}
+	}
+
+	@Command public void server_url(IPrintWriter out,Interpreter interp) {
+		out.print( interp.encode( Jtp.defaultContextUrl() ) );
+	}
+
+	@Command public void javascript_version(IPrintWriter out,Interpreter interp) {
+		out.print(Shared.javascriptVersion);
+	}
+
+	@Command public void css_version(IPrintWriter out,Interpreter interp) {
+		out.print(Shared.cssVersion);
+	}
+
+}