Index: src/clone.c ================================================================== --- src/clone.c +++ src/clone.c @@ -141,11 +141,11 @@ }else{ db_create_repository(g.argv[3]); db_open_repository(g.argv[3]); db_begin_transaction(); db_record_repository_filename(g.argv[3]); - db_initial_setup(0, zDefaultUser, 0); + db_initial_setup(0, 0, zDefaultUser, 0); user_select(); db_set("content-schema", CONTENT_SCHEMA, 0); db_set("aux-schema", AUX_SCHEMA, 0); db_set("last-sync-url", g.argv[2], 0); if( g.zSSLIdentity!=0 ){ Index: src/configure.c ================================================================== --- src/configure.c +++ src/configure.c @@ -157,10 +157,32 @@ } } } return 0; } + +/* +** Return a pointer to a string that contains the RHS of an IN operator +** that will select CONFIG table names that are part of the configuration +** that matchines iMatch. +*/ +const char *configure_inop_rhs(int iMask){ + Blob x; + int i; + const char *zSep = ""; + + blob_zero(&x); + blob_append(&x, "(", 1); + for(i=0; i<count(aConfig); i++){ + if( (aConfig[i].groupMask & iMask)==0 ) continue; + if( aConfig[i].zName[0]=='@' ) continue; + blob_appendf(&x, "%s'%s'", zSep, aConfig[i].zName); + zSep = ","; + } + blob_append(&x, ")", 1); + return blob_str(&x); +} /* ** Return the mask for the named configuration parameter if it can be ** safely exported. Return 0 if the parameter is not safe to export. ** Index: src/db.c ================================================================== --- src/db.c +++ src/db.c @@ -701,10 +701,25 @@ sqlite3_wal_autocheckpoint(db, 1); /* Set to checkpoint frequently */ sqlite3_create_function(db, "now", 0, SQLITE_ANY, 0, db_now_function, 0, 0); return db; } + +/* +** Detaches the zLabel database. +*/ +void db_detach(const char *zLabel){ + db_multi_exec("DETACH DATABASE %s", zLabel); +} + +/* +** zDbName is the name of a database file. Attach zDbName using +** the name zLabel. +*/ +void db_attach(const char *zDbName, const char *zLabel){ + db_multi_exec("ATTACH DATABASE %Q AS %s", zDbName, zLabel); +} /* ** zDbName is the name of a database file. If no other database ** file is open, then open this one. If another database file is ** already open, then attach zDbName using the name zLabel. @@ -713,11 +728,11 @@ if( !g.db ){ g.db = openDatabase(zDbName); g.zMainDbType = zLabel; db_connection_init(); }else{ - db_multi_exec("ATTACH DATABASE %Q AS %s", zDbName, zLabel); + db_attach(zDbName, zLabel); } } /* ** Open the user database in "~/.fossil". Create the database anew if @@ -1206,23 +1221,51 @@ "INSERT INTO user(login,pw,cap,info)" " VALUES('reader','','kptw','Reader');" ); } } + +/* +** Return a pointer to a string that contains the RHS of an IN operator +** that will select CONFIG table names that are in the list of control +** settings. +*/ +const char *db_setting_inop_rhs(){ + Blob x; + int i; + const char *zSep = ""; + + blob_zero(&x); + blob_append(&x, "(", 1); + for(i=0; ctrlSettings[i].name; i++){ + blob_appendf(&x, "%s'%s'", zSep, ctrlSettings[i].name); + zSep = ","; + } + blob_append(&x, ")", 1); + return blob_str(&x); +} /* ** Fill an empty repository database with the basic information for a ** repository. This function is shared between 'create_repository_cmd' ** ('new') and 'reconstruct_cmd' ('reconstruct'), both of which create ** new repositories. +** +** The zTemplate parameter determines if the settings for the repository +** should be copied from another repository. If zTemplate is 0 then the +** settings will have their normal default values. If zTemplate is +** non-zero, it is assumed that the caller of this function has already +** attached a database using the label "settingSrc". If not, the call to +** this function will fail. ** ** The zInitialDate parameter determines the date of the initial check-in ** that is automatically created. If zInitialDate is 0 then no initial ** check-in is created. The makeServerCodes flag determines whether or ** not server and project codes are invented for this repository. */ void db_initial_setup( + const char *zTemplate, /* Repository from which to copy settings. */ const char *zInitialDate, /* Initial date of repository. (ex: "now") */ const char *zDefaultUser, /* Default user for the repository */ int makeServerCodes /* True to make new server & project codes */ ){ char *zDate; @@ -1241,10 +1284,47 @@ } if( !db_is_global("autosync") ) db_set_int("autosync", 1, 0); if( !db_is_global("localauth") ) db_set_int("localauth", 0, 0); db_create_default_users(0, zDefaultUser); user_select(); + + if( zTemplate ){ + /* + ** Copy all settings from the supplied template repository. + */ + db_multi_exec( + "INSERT OR REPLACE INTO config" + " SELECT name,value,mtime FROM settingSrc.config" + " WHERE (name IN %s OR name IN %s)" + " AND name NOT GLOB 'project-*';", + configure_inop_rhs(CONFIGSET_ALL), + db_setting_inop_rhs() + ); + db_multi_exec( + "REPLACE INTO reportfmt SELECT * FROM settingSrc.reportfmt;" + ); + + /* + ** Copy the user permissions, contact information, last modified + ** time, and photo for all the "system" users from the supplied + ** template repository into the one being setup. The other columns + ** are not copied because they contain security information or other + ** data specific to the other repository. The list of columns copied + ** by this SQL statement may need to be revised in the future. + */ + db_multi_exec("UPDATE user SET" + " cap = (SELECT u2.cap FROM settingSrc.user u2" + " WHERE u2.login = user.login)," + " info = (SELECT u2.info FROM settingSrc.user u2" + " WHERE u2.login = user.login)," + " mtime = (SELECT u2.mtime FROM settingSrc.user u2" + " WHERE u2.login = user.login)," + " photo = (SELECT u2.photo FROM settingSrc.user u2" + " WHERE u2.login = user.login)" + " WHERE user.login IN ('anonymous','nobody','developer','reader');" + ); + } if( zInitialDate ){ int rid; blob_zero(&manifest); blob_appendf(&manifest, "C initial\\sempty\\scheck-in\n"); @@ -1277,33 +1357,47 @@ ** ** By default, your current login name is used to create the default ** admin user. This can be overridden using the -A|--admin-user ** parameter. ** +** By default, all settings will be initialized to their default values. +** This can be overridden using the --template parameter to specify a +** repository file from which to copy the initial settings. When a template +** repository is used, almost all of the settings accessible from the setup +** page, either directly or indirectly, will be copied. Normal users and +** their associated permissions will not be copied; however, the system +** default users "anonymous", "nobody", "reader", "developer", and their +** associated permissions will be copied. +** ** Options: +** --template FILE copy settings from repository file ** --admin-user|-A USERNAME select given USERNAME as admin user ** --date-override DATETIME use DATETIME as time of the initial checkin ** ** See also: clone */ void create_repository_cmd(void){ char *zPassword; + const char *zTemplate; /* Repository from which to copy settings */ const char *zDate; /* Date of the initial check-in */ const char *zDefaultUser; /* Optional name of the default user */ + zTemplate = find_option("template",0,1); zDate = find_option("date-override",0,1); zDefaultUser = find_option("admin-user","A",1); if( zDate==0 ) zDate = "now"; if( g.argc!=3 ){ usage("REPOSITORY-NAME"); } db_create_repository(g.argv[2]); db_open_repository(g.argv[2]); db_open_config(0); + if( zTemplate ) db_attach(zTemplate, "settingSrc"); db_begin_transaction(); - db_initial_setup(zDate, zDefaultUser, 1); + db_initial_setup(zTemplate, zDate, zDefaultUser, 1); db_end_transaction(0); + if( zTemplate ) db_detach("settingSrc"); fossil_print("project-id: %s\n", db_get("project-code", 0)); fossil_print("server-id: %s\n", db_get("server-code", 0)); zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin); fossil_print("admin-user: %s (initial password is \"%s\")\n", g.zLogin, zPassword); Index: src/import.c ================================================================== --- src/import.c +++ src/import.c @@ -771,11 +771,11 @@ "CREATE TEMP TABLE xtag(tname TEXT UNIQUE, tcontent TEXT);" ); db_begin_transaction(); - if( !incrFlag ) db_initial_setup(0, 0, 1); + if( !incrFlag ) db_initial_setup(0, 0, 0, 1); git_fast_import(pIn); db_prepare(&q, "SELECT tcontent FROM xtag"); while( db_step(&q)==SQLITE_ROW ){ Blob record; db_ephemeral_blob(&q, 0, &record); Index: src/login.c ================================================================== --- src/login.c +++ src/login.c @@ -1444,21 +1444,21 @@ if( rc ) return; /* Attach the other repository. Make sure the username/password is ** valid and has Setup permission. */ - db_multi_exec("ATTACH %Q AS other", zRepo); + db_attach(zRepo, "other"); zOtherProjCode = db_text("x", "SELECT value FROM other.config" " WHERE name='project-code'"); zPwHash = sha1_shared_secret(zPassword, zLogin, zOtherProjCode); if( !db_exists( "SELECT 1 FROM other.user" " WHERE login=%Q AND cap GLOB '*s*'" " AND (pw=%Q OR pw=%Q)", zLogin, zPassword, zPwHash) ){ - db_multi_exec("DETACH other"); + db_detach("other"); *pzErrMsg = "The supplied username/password does not correspond to a" " user Setup permission on the other repository."; return; } Index: src/rebuild.c ================================================================== --- src/rebuild.c +++ src/rebuild.c @@ -886,11 +886,11 @@ } db_create_repository(g.argv[2]); db_open_repository(g.argv[2]); db_open_config(0); db_begin_transaction(); - db_initial_setup(0, 0, 1); + db_initial_setup(0, 0, 0, 1); fossil_print("Reading files from directory \"%s\"...\n", g.argv[3]); recon_read_dir(g.argv[3]); fossil_print("\nBuilding the Fossil repository...\n");