view src/nabble/view/web/template/NabbleNamespace.java @ 35:5ea557eece1f

remove site.isNew()
author Franklin Schmidt <fschmidt@gmail.com>
date Tue, 07 Jul 2020 09:57:53 -0600
parents b0e75dfe1853
children 72765b66e2c3
line wrap: on
line source

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 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 debug = new CommandSpec.Builder()
		.dotParameter("text")
		.outputtedParameters()
		.build()
	;

	@Command public void debug(IPrintWriter out,Interpreter interp) {
		interp.setEncoder(Encoder.TEXT);
		String text = interp.getArgString("text");
		System.out.println(text);
	}


	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);
	}

}