- Improved handling if URLs in writing them to output - Updated MB examplemaster
@@ -553,9 +553,9 @@ class url_block: | |||
if m.group(3): | |||
params=make_format_params(m.group(4),'urlencode') | |||
if not params: | |||
output( "out()<<content.rendering_application().url(%s);" % s) | |||
output( "content.rendering_application().mapper().map(out(),%s);" % s) | |||
else: | |||
output( "out()<<content.rendering_application().url(%s, %s);" % (s , ', '.join(params))) | |||
output( "content.rendering_application().mapper().map(out(),%s, %s);" % (s , ', '.join(params))) | |||
class include_block: | |||
@@ -185,67 +185,70 @@ namespace cppcms { | |||
/// | |||
void render(std::string skin,std::string template_name,std::ostream &out,base_content &content); | |||
/// | |||
/// Render a template \a template_name of default skin using this application as content. | |||
/// Register an application \a app as child. Ownership of app is not transfered to parent, however | |||
/// it would shared it's parent reference count. | |||
/// | |||
/// Side effect requires: output stream for response class, causes all updated session | |||
/// data be saved and all headers be written. You can't change headers after calling this function. | |||
void add(application &app); | |||
/// | |||
void render(std::string template_name); | |||
/// Register an application \a app as child and mount it into url dispatched calling | |||
/// dispatcher().mount(regex,app,part); | |||
/// | |||
/// Render a template \a template_name of \a skin skin using this application as content | |||
/// Ownership of app is not transfered to parent, however | |||
/// it would shared it's parent reference count. | |||
/// | |||
/// Side effect requires: output stream for response class, causes all updated session | |||
/// data be saved and all headers be written. You can't change headers after calling this function. | |||
/// | |||
void render(std::string skin,std::string template_name); | |||
void add(application &app,std::string const ®ex,int part); | |||
/// | |||
/// Register an application \a app as child. Ownership of app is not transfered to parent, however | |||
/// it would shared it's parent reference count. | |||
/// Register an application \a app as child and mount it into: | |||
/// | |||
void add(application &app); | |||
/// - url_dispatcher calling dispatcher().mount(regex,app,part); | |||
/// - url_mapper calling mapper().mount(name,url,app); | |||
/// | |||
/// Register an application \a app as child. Ownership of app is not transfered to parent, however | |||
/// it would shared it's parent reference count. | |||
/// Ownership of app is not transfered to parent, however | |||
/// it would shared it's parent reference count. | |||
/// | |||
/// All URL that match regular expression \a regex would be passed to the child for match. Matched part | |||
/// \a part would be used by child for matching. | |||
/// | |||
/// For example: | |||
void add(application &app,std::string const &name,std::string const &url,std::string const ®ex,int part); | |||
/// | |||
/// \code | |||
/// add(users,"^/users(.*)$",1") | |||
/// \endcode | |||
/// Register an application \a app as child and mount it into | |||
/// url_mapper calling mapper().mount(name,url,app); | |||
/// | |||
/// For URL /users/moshe would pass only "/moshe" to URL dispatched of \a users object | |||
/// Ownership of app is not transfered to parent, however | |||
/// it would shared it's parent reference count. | |||
/// | |||
void add(application &app,std::string regex,int part); | |||
/// | |||
void add(application &app,std::string const &name,std::string const &url); | |||
/// | |||
/// Register an application \a app as child. Ownership of app is transfered to parent | |||
/// | |||
void attach(application *app); | |||
/// | |||
/// Register an application \a app as child. Ownership of app is transfered to parent | |||
/// Register an application \a app as child and mount it into | |||
/// url_dispatcher calling dispatcher().mount(regex,*app,part); | |||
/// | |||
/// Ownership of app is transfered to parent. | |||
/// | |||
void attach(application *app,std::string const ®ex,int part); | |||
/// | |||
/// Register an application \a app as child and mount it into | |||
/// url_mapper calling mapper().mount(name,url,*app); | |||
/// | |||
/// Ownership of app is transfered to parent. | |||
/// | |||
/// All URL that match regular expression \a regex would be passed to the child for match. Matched part | |||
/// \a part would be used by child for matching. | |||
void attach(application *app,std::string const &name,std::string const &url); | |||
/// | |||
/// For example: | |||
/// Register an application \a app as child and mount it into: | |||
/// | |||
/// \code | |||
/// add(users,"^/users(.*)$",1") | |||
/// \endcode | |||
/// - url_dispatcher calling dispatcher().mount(regex,*app,part); | |||
/// - url_mapper calling mapper().mount(name,url,*app); | |||
/// | |||
/// For URL /users/moshe would pass only "/moshe" to URL dispatched of \a users object | |||
/// Ownership of app is transfered to parent. | |||
/// | |||
void attach(application *app,std::string regex,int part); | |||
void attach(application *app,std::string const &name,std::string const &url,std::string const ®ex,int part); | |||
/// | |||
/// Get the parent of the application, if the application is the topmost class in hierarchy, | |||
@@ -44,6 +44,7 @@ namespace cppcms { | |||
void root(std::string const &r); | |||
void assign(std::string const &key,std::string const &url); | |||
void assign(std::string const &url); | |||
void set_value(std::string const &key,std::string const &value); | |||
void clear_value(std::string const &key); | |||
@@ -23,5 +23,6 @@ | |||
#include <cppcms/base_view.h> | |||
#include <cppcms/views_pool.h> | |||
#include <cppcms/application.h> | |||
#include <cppcms/url_mapper.h> | |||
#endif | |||
@@ -26,6 +26,9 @@ namespace apps { | |||
forums::forums(cppcms::service &srv) : master(srv) | |||
{ | |||
mapper().assign("{1}"); // with id | |||
mapper().assign(""); // default | |||
dispatcher().assign(".*",&forums::prepare,this,0); | |||
} | |||
void forums::prepare_content(data::forums &c,std::string const &page) | |||
@@ -10,39 +10,33 @@ | |||
namespace apps { | |||
mb::mb(cppcms::service &s) : | |||
cppcms::application(s) | |||
mb::mb(cppcms::service &s) : cppcms::application(s) | |||
{ | |||
mapper().root(settings().get<std::string>("mb.root")); | |||
// Forums | |||
forums *forums_ptr = new forums(s); | |||
attach(forums_ptr); | |||
dispatcher().assign("(/(\\d+)?)?",&forums::prepare,forums_ptr,2); | |||
mapper().assign("forums","/"); | |||
mapper().assign("forums","/{1}"); | |||
// Flat thread | |||
flat_thread *flat_ptr = new flat_thread(s); | |||
attach(flat_ptr); | |||
dispatcher().assign("/flat/(\\d+)",&flat_thread::prepare,flat_ptr,1); | |||
mapper().assign("flat_thread","/flat/{1}"); | |||
// Tree thread | |||
tree_thread *tree_ptr = new tree_thread(s); | |||
attach(tree_ptr); | |||
dispatcher().assign("/tree/(\\d+)",&tree_thread::prepare,tree_ptr,1); | |||
mapper().assign("tree_thread","/tree/{1}"); | |||
attach( new forums(s), | |||
"forums", | |||
"/{1}", | |||
"(/(\\d+)?)?",2); | |||
attach( new flat_thread(s), | |||
"flat_thread", | |||
"/flat/{1}", | |||
"/flat/(\\d+)",1); | |||
attach( new tree_thread(s), | |||
"tree_thread", | |||
"/tree/{1}", | |||
"/tree/(\\d+)",1); | |||
attach( new reply(s), | |||
"comment", | |||
"/comment/{1}", | |||
"/comment/(\\d+)",1); | |||
// Generic mapping | |||
mapper().root(settings().get<std::string>("mb.root")); | |||
mapper().assign("user_thread","/{method}/{1}"); | |||
// Reply | |||
reply *repl_ptr = new reply(s); | |||
attach(repl_ptr); | |||
dispatcher().assign("/comment/(\\d+)",&reply::prepare,repl_ptr,1); | |||
mapper().assign("comment","/comment/{1}"); | |||
} | |||
} // apps |
@@ -3,11 +3,11 @@ | |||
#include <cppcms/application.h> | |||
namespace apps { | |||
class mb : public cppcms::application { | |||
public: | |||
mb(cppcms::service &s); | |||
}; | |||
@@ -68,7 +68,8 @@ bool thread_shared::prepare(data::thread_shared &c,int id) | |||
flat_thread::flat_thread(cppcms::service &s) : thread_shared(s) | |||
{ | |||
dispatcher().assign(".*",&flat_thread::prepare,this,0); | |||
mapper().assign("{1}"); | |||
} | |||
@@ -125,6 +126,8 @@ void make_tree(data::tree_t &messages,std::map<int,std::map<int,data::msg> > &co | |||
tree_thread::tree_thread(cppcms::service &s) : thread_shared(s) | |||
{ | |||
dispatcher().assign(".*",&tree_thread::prepare,this,0); | |||
mapper().assign("{1}"); | |||
} | |||
@@ -166,6 +169,8 @@ void tree_thread::prepare(std::string sid) | |||
reply::reply(cppcms::service &srv) : thread_shared(srv) | |||
{ | |||
dispatcher().assign(".*",&reply::prepare,this,0); | |||
mapper().assign("{1}"); | |||
} | |||
void reply::prepare(std::string smid) | |||
@@ -11,7 +11,7 @@ | |||
<h2><% gt "No Topics" %></h2> | |||
<% end %> | |||
<h2><% gt "Create New Disussion" %></h2> | |||
<form action="" method="post" > | |||
<form action="<% url "/forums" %>" method="post" > | |||
<% form as_p form %> | |||
</form> | |||
<% end template %> | |||
@@ -166,11 +166,26 @@ void application::add(application &app) | |||
if(app.parent()!=this) | |||
app.parent(this); | |||
} | |||
void application::add(application &app,std::string regex,int part) | |||
void application::add(application &app,std::string const ®ex,int part) | |||
{ | |||
add(app); | |||
dispatcher().mount(regex,app,part); | |||
} | |||
void application::add(application &app,std::string const &name,std::string const &url) | |||
{ | |||
add(app); | |||
mapper().mount(name,url,app); | |||
} | |||
void application::add(application &app,std::string const &name,std::string const &url,std::string const ®ex,int part) | |||
{ | |||
add(app); | |||
dispatcher().mount(regex,app,part); | |||
mapper().mount(name,url,app); | |||
} | |||
void application::attach(application *app) | |||
{ | |||
d->managed_children.push_back(app); | |||
@@ -208,10 +223,21 @@ void application::main(std::string url) | |||
} | |||
} | |||
void application::attach(application *app,std::string regex,int part) | |||
void application::attach(application *app,std::string const ®ex,int part) | |||
{ | |||
d->managed_children.push_back(app); | |||
add(*app,regex,part); | |||
attach(app); | |||
dispatcher().mount(regex,*app,part); | |||
} | |||
void application::attach(application *app,std::string const &name,std::string const &url) | |||
{ | |||
attach(app); | |||
mapper().mount(name,url,*app); | |||
} | |||
void application::attach(application *app,std::string const &name,std::string const &url,std::string const ®ex,int part) | |||
{ | |||
attach(app); | |||
dispatcher().mount(regex,*app,part); | |||
mapper().mount(name,url,*app); | |||
} | |||
void application::render(std::string template_name,base_content &content) | |||
@@ -226,29 +252,6 @@ void application::render(std::string skin,std::string template_name,base_content | |||
service().views_pool().render(skin,template_name,response().out(),content); | |||
} | |||
void application::render(std::string template_name) | |||
{ | |||
base_content *cnt = dynamic_cast<base_content *>(this); | |||
if(!cnt) { | |||
throw cppcms_error( "Can't use application::render(std::string) when the application " | |||
"is not derived from base_content"); | |||
} | |||
render(template_name,*cnt); | |||
} | |||
void application::render(std::string skin,std::string template_name) | |||
{ | |||
base_content *cnt = dynamic_cast<base_content *>(this); | |||
if(!cnt) { | |||
throw cppcms_error( "Can't use application::render(std::string,std::string) when the application " | |||
"is not derived from base_content"); | |||
} | |||
render(skin,template_name,*cnt); | |||
} | |||
void application::render(std::string template_name,std::ostream &out,base_content &content) | |||
{ | |||
rnd_guard g(content,this); | |||
@@ -24,6 +24,8 @@ | |||
#include <stdlib.h> | |||
#include <iostream> | |||
namespace cppcms { | |||
struct url_mapper::data | |||
{ | |||
@@ -52,12 +54,25 @@ namespace cppcms { | |||
{ | |||
by_key_type::const_iterator kp = by_key.find(key); | |||
if(kp == by_key.end()) | |||
throw cppcms_error("url_mapper: key " + key + " not found"); | |||
throw cppcms_error("url_mapper: key `" + key + "' not found"); | |||
by_size_type::const_iterator sp = kp->second.find(params_no); | |||
if(sp == kp->second.end()) | |||
throw cppcms_error("url_mapper: invalid number of parameters for " + key); | |||
return sp->second; | |||
} | |||
url_mapper *is_app(std::string const &key) const | |||
{ | |||
by_key_type::const_iterator kp = by_key.find(key); | |||
if(kp == by_key.end()) | |||
return 0; | |||
by_size_type::const_iterator sp = kp->second.begin(); | |||
if(sp==kp->second.end()) | |||
return 0; | |||
if(sp->second.child) | |||
return &sp->second.child->mapper(); | |||
return 0; | |||
} | |||
url_mapper &child(std::string const &name) | |||
{ | |||
@@ -109,8 +124,12 @@ namespace cppcms { | |||
} | |||
} | |||
if(parent) { | |||
parent->mapper().map(output,this_name,ss.str()); | |||
std::string url = ss.str(); | |||
filters::streamable stream_url(url); | |||
filters::streamable const *par_ptr=&stream_url; | |||
parent->mapper().d->map(this_name,&par_ptr,1,data_helpers,output); | |||
} | |||
} | |||
@@ -119,10 +138,6 @@ namespace cppcms { | |||
void url_mapper::assign(std::string const &key,std::string const &url) | |||
{ | |||
real_assign(key,url,0); | |||
} | |||
void url_mapper::real_assign(std::string const &key,std::string const &url,application *child) | |||
{ | |||
if( key.empty() | |||
|| key.find('/') != std::string::npos | |||
|| key ==".." | |||
@@ -130,6 +145,14 @@ namespace cppcms { | |||
{ | |||
throw cppcms_error("cppcms::url_mapper: key may not be '' , '.' or '..' and must not include '/' in it"); | |||
} | |||
real_assign(key,url,0); | |||
} | |||
void url_mapper::assign(std::string const &url) | |||
{ | |||
real_assign(std::string(),url,0); | |||
} | |||
void url_mapper::real_assign(std::string const &key,std::string const &url,application *child) | |||
{ | |||
data::entry e; | |||
std::string::const_iterator prev = url.begin(), p = url.begin(); | |||
@@ -183,7 +206,25 @@ namespace cppcms { | |||
throw cppcms_error("cppcms::url_mapper the application mapping should use only 1 parameter"); | |||
} | |||
e.parts.push_back(std::string(prev,p)); | |||
e.child = child; | |||
if(child) { | |||
if(d->by_key.find(key)!=d->by_key.end()) | |||
throw cppcms_error( "cppcms::url_mapper: mounted application key `" + key + | |||
"' can't be shared with ordinary url key"); | |||
} | |||
else { | |||
data::by_key_type::iterator p = d->by_key.find(key); | |||
if(p!=d->by_key.end()) { | |||
data::by_size_type::const_iterator p2 = p->second.find(1); | |||
if(p2!=p->second.end() && p2->second.child) | |||
{ | |||
throw cppcms_error( "cppcms::url_mapper: ordinary url key `"+key+ | |||
"can't be shared with mounted application key"); | |||
} | |||
} | |||
} | |||
d->by_key[key][max_index] = e; | |||
} | |||
@@ -258,6 +299,12 @@ namespace cppcms { | |||
size_t end = key.find('/',pos); | |||
if(end == std::string::npos) { | |||
real_key = key.substr(pos); | |||
url_mapper *tmp = mapper->d->is_app(real_key); | |||
if(tmp) { | |||
// empty special key | |||
real_key.clear(); | |||
return *tmp; | |||
} | |||
return *mapper; | |||
} | |||
size_t chunk_size = end - pos; | |||
@@ -125,7 +125,8 @@ void basic_test() | |||
struct test1 : public cppcms::application { | |||
test1(cppcms::service &s) : cppcms::application(s) | |||
{ | |||
mapper().assign("default","/"); | |||
mapper().assign("/default"); | |||
mapper().assign("somepath","/"); | |||
mapper().assign("page","/{1}"); | |||
mapper().assign("preview","/{1}/preview"); | |||
mapper().assign("bylang","/{lang}"); | |||
@@ -138,7 +139,7 @@ struct test2 : public cppcms::application { | |||
cppcms::application(s), | |||
bee(s) | |||
{ | |||
mapper().assign("default","/"); | |||
mapper().assign("somepath","/"); | |||
mapper().assign("page","/{1}"); | |||
mapper().assign("preview","/{1}/preview"); | |||
mapper().mount("bee","/bee{1}",bee); | |||
@@ -161,7 +162,7 @@ struct test_app : public cppcms::application { | |||
mapper().mount("foo","/foo{1}",foo); | |||
mapper().mount("bar","/bar{1}",bar); | |||
mapper().mount("foobar","/foobar{1}",bee); | |||
mapper().assign("default","/test"); | |||
mapper().assign("somepath","/test"); | |||
mapper().root("xx"); | |||
bee.bee.mapper().set_value("lang","en"); | |||
@@ -169,31 +170,32 @@ struct test_app : public cppcms::application { | |||
void test_hierarchy() | |||
{ | |||
std::ostringstream ss; | |||
mapper().map(ss,"default"); | |||
mapper().map(ss,"somepath"); | |||
TEST(value(ss) == "xx/test"); | |||
foo.mapper().map(ss,"default"); | |||
foo.mapper().map(ss,"somepath"); | |||
TEST(value(ss) == "xx/foo/"); | |||
foo.mapper().map(ss,"page",1); | |||
TEST(value(ss) == "xx/foo/1"); | |||
foo.mapper().map(ss,"preview",1); | |||
TEST(value(ss) == "xx/foo/1/preview"); | |||
bar.mapper().map(ss,"default"); | |||
bar.mapper().map(ss,"somepath"); | |||
TEST(value(ss) == "xx/bar/"); | |||
bar.mapper().map(ss,"page",1); | |||
TEST(value(ss) == "xx/bar/1"); | |||
bar.mapper().map(ss,"preview",1); | |||
TEST(value(ss) == "xx/bar/1/preview"); | |||
bee.mapper().map(ss,"default"); | |||
bee.mapper().map(ss,"somepath"); | |||
TEST(value(ss) == "xx/foobar/"); | |||
bee.mapper().map(ss,"page",1); | |||
TEST(value(ss) == "xx/foobar/1"); | |||
bee.mapper().map(ss,"preview",1); | |||
TEST(value(ss) == "xx/foobar/1/preview"); | |||
bee.bee.mapper().map(ss,"default"); | |||
bee.bee.mapper().map(ss,"somepath"); | |||
TEST(value(ss) == "xx/foobar/bee/"); | |||
bee.bee.mapper().map(ss,"page",1); | |||
TEST(value(ss) == "xx/foobar/bee/1"); | |||
@@ -212,31 +214,36 @@ struct test_app : public cppcms::application { | |||
} | |||
void test_mapping() | |||
{ | |||
TEST(u(*this,"/default")=="xx/test"); | |||
TEST(u(*this,"/foo/default")=="xx/foo/"); | |||
TEST(u(*this,"/foobar/bee/default")=="xx/foobar/bee/"); | |||
TEST(u(*this,"/somepath")=="xx/test"); | |||
TEST(u(*this,"/foo/somepath")=="xx/foo/"); | |||
TEST(u(*this,"/foo/")=="xx/foo/default"); | |||
TEST(u(*this,"/foo")=="xx/foo/default"); | |||
TEST(u(*this,"/foobar/bee/")=="xx/foobar/bee/default"); | |||
TEST(u(*this,"/foobar/bee")=="xx/foobar/bee/default"); | |||
TEST(u(*this,"/foobar/bee/bylang")=="xx/foobar/bee/en"); | |||
TEST(u(*this,"default")=="xx/test"); | |||
TEST(u(*this,"foo/default")=="xx/foo/"); | |||
TEST(u(*this,"foobar/bee/default")=="xx/foobar/bee/"); | |||
TEST(u(*this,"somepath")=="xx/test"); | |||
TEST(u(*this,"foo/somepath")=="xx/foo/"); | |||
TEST(u(*this,"foobar/bee/somepath")=="xx/foobar/bee/"); | |||
TEST(u(*this,"foobar/bee/bylang")=="xx/foobar/bee/en"); | |||
TEST(u(bar,"/default")=="xx/test"); | |||
TEST(u(bar,"/foo/default")=="xx/foo/"); | |||
TEST(u(bar,"/foobar/bee/default")=="xx/foobar/bee/"); | |||
TEST(u(bar,"/somepath")=="xx/test"); | |||
TEST(u(bar,"/foo/somepath")=="xx/foo/"); | |||
TEST(u(bar,"/foobar/bee/somepath")=="xx/foobar/bee/"); | |||
TEST(u(bar,"/foobar/bee/")=="xx/foobar/bee/default"); | |||
TEST(u(bar,"/foobar/bee")=="xx/foobar/bee/default"); | |||
TEST(u(bar,"/foobar/bee/bylang")=="xx/foobar/bee/en"); | |||
TEST(u(bee.bee,"/default")=="xx/test"); | |||
TEST(u(bee.bee,"/foo/default")=="xx/foo/"); | |||
TEST(u(bee.bee,"/foobar/bee/default")=="xx/foobar/bee/"); | |||
TEST(u(bee.bee,"/somepath")=="xx/test"); | |||
TEST(u(bee.bee,"/foo/somepath")=="xx/foo/"); | |||
TEST(u(bee.bee,"/foobar/bee/somepath")=="xx/foobar/bee/"); | |||
TEST(u(bee.bee,"/foobar/bee/bylang")=="xx/foobar/bee/en"); | |||
TEST(u(bee.bee,"default")=="xx/foobar/bee/"); | |||
TEST(u(bee.bee,"somepath")=="xx/foobar/bee/"); | |||
TEST(u(bee.bee,"bylang")=="xx/foobar/bee/en"); | |||
TEST(u(bee.bee,"../default")=="xx/foobar/"); | |||
TEST(u(bee.bee,"../../default")=="xx/test"); | |||
TEST(u(bee.bee,"../somepath")=="xx/foobar/"); | |||
TEST(u(bee.bee,"../../somepath")=="xx/test"); | |||
TEST(u(bee.bee,"bylang")=="xx/foobar/bee/en"); | |||
TEST(u(foo,"../foobar/bee/bylang")=="xx/foobar/bee/en"); | |||
} | |||