func exportRole(id int64) error { // Split the filename and ensure the directory exists path, name := splitFilename(strconv.FormatInt(id, 10)) path = config.Export.OutputDirectory + f.RolesPath + path if !fileExists(path) { err := mkDirAll(path) if err != nil { return err } } filename := fmt.Sprintf("%s/%s.json", path, name) // Don't export if we've exported already if fileExists(filename) { return nil } var ( publicGroup int64 genericOptions int64 ) vb := vbUserGroup{} err := db.QueryRow(` SELECT usergroupid ,title ,description ,forumpermissions ,ispublicgroup ,genericoptions FROM `+config.DB.TablePrefix+`usergroup WHERE usergroupid = ?`, id, ).Scan( &vb.UserGroupID, &vb.Title, &vb.Description, &vb.ForumPermissions, &publicGroup, &genericOptions, ) if err != nil { return err } ex := f.Role{} ex.ID = vb.UserGroupID ex.Name = vb.Title ex.Text = vb.Description if publicGroup == 0 { ex.DefaultRole = true } // From vBulletin includes/xml/bitfield_vbulletin.xml // <group name="genericoptions"> // <bitfield name="showgroup" >1</bitfield> // <bitfield name="showbirthday" >2</bitfield> // <bitfield name="showmemberlist" >4</bitfield> // <bitfield name="showeditedby" >8</bitfield> // <bitfield name="allowmembergroups" >16</bitfield> // <bitfield name="isnotbannedgroup" >32</bitfield> // <bitfield name="requirehvcheck" >64</bitfield> // </group> ex.Banned = genericOptions&32 == 0 // From vBulletin includes/xml/bitfield_vbulletin.xml // <group name="forumpermissions"> // <bitfield name="canview" >1</bitfield> // <bitfield name="canviewthreads" >524288</bitfield> // <bitfield name="canviewothers" >2</bitfield> // <bitfield name="cansearch" >4</bitfield> // <bitfield name="canemail" >8</bitfield> // <bitfield name="canpostnew" >16</bitfield> // <bitfield name="canreplyown" >32</bitfield> // <bitfield name="canreplyothers" >64</bitfield> // <bitfield name="caneditpost" >128</bitfield> // <bitfield name="candeletepost" >256</bitfield> // <bitfield name="candeletethread" >512</bitfield> // <bitfield name="canopenclose" >1024</bitfield> // <bitfield name="canmove" >2048</bitfield> // <bitfield name="cangetattachment" >4096</bitfield> // <bitfield name="canpostattachment" >8192</bitfield> // <bitfield name="attachlimit" >1</bitfield> // <bitfield name="canpostpoll" >16384</bitfield> // <bitfield name="canvote" >32768</bitfield> // <bitfield name="canthreadrate" >65536</bitfield> // <bitfield name="followforummoderation" >131072</bitfield> // <bitfield name="canseedelnotice" >262144</bitfield> // <bitfield name="cantagown" >1048576</bitfield> // <bitfield name="cantagothers" >2097152</bitfield> // <bitfield name="candeletetagown" >4194304</bitfield> // <bitfield name="canseethumbnails" >8388608</bitfield> // </group> perms := f.ForumPermissions{} perms.View = vb.ForumPermissions&1 != 0 perms.PostNew = vb.ForumPermissions&16 != 0 perms.EditOwn = vb.ForumPermissions&128 != 0 perms.EditOthers = false perms.DeleteOwn = vb.ForumPermissions&256 != 0 perms.DeleteOthers = false perms.CloseOwn = vb.ForumPermissions&1024 != 0 perms.OpenOwn = vb.ForumPermissions&1024 != 0 ex.ForumPermissions = perms // If we are usergroup 1 or 2 then we are the default built-in ones and we // can handle that and get out. switch id { case 1: // Guests in vBulletin ex.IncludeGuests = true err = writeFile(filename, ex) addExportFile(ex.ID, filename) return err case 2: // Registered users in vBulletin ex.IncludeRegistered = true err = writeFile(filename, ex) addExportFile(ex.ID, filename) return err case 3, 4: // Awaiting email confirmation, covered by group id == 2 return nil case 5, 6, 7: ex.Moderator = true } rows, err := db.Query(` SELECT date ,posts ,strategy FROM `+config.DB.TablePrefix+`userpromotion WHERE joinusergroupid = ?`, id, ) if err != nil { return err } defer rows.Close() type Promotion struct { Date int64 Posts int64 Strategy int64 } promotions := []Promotion{} hasCriteria := false for rows.Next() { promotion := Promotion{} err = rows.Scan( &promotion.Date, &promotion.Posts, &promotion.Strategy, ) if err != nil { return err } if promotion.Strategy != 16 { hasCriteria = true } promotions = append(promotions, promotion) } err = rows.Err() if err != nil { return err } rows.Close() // date,posts,strategy // 1,3,3 // 30,1500,17 if hasCriteria { // Export criteria var OrGroup int64 ex.Criteria = []f.Criterion{} // Note that strategy translates to the following: // // 0 = Posts and Reputation and Date // 1 = Posts or Reputation or Date // 2 = (Posts and Reputation) or Date // 3 = Posts and (Reputation or Date) // 4 = (Posts or Reputation) and Date // 5 = Posts or (Reputation and Date) // 6 = Reputation and (Posts or Date) // 7 = Reputation or (Posts and Date) // 16 = Reputation // 17 = Posts // 18 = Join Date // // Based on the strategy we can ignore certain fields, i.e. strategy 17 // means we only care about the number of posts // // We are ignoring reputation as a criteria and so the strategies are // equivalent to: // null // 16 // comments // 17 // date // 18 // comments AND date // 0, 3, 4, 7 // comments OR date // 1, 2, 5, 6 const ( postsKey string = "commentCount" dateKey string = "daysRegistered" ) for _, promotion := range promotions { switch promotion.Strategy { case 17: // Just posts ex.Criteria = append(ex.Criteria, f.Criterion{ OrGroup: OrGroup, Key: postsKey, Predicate: f.PredicateGreaterThanOrEquals, Value: promotion.Posts, }) case 18: // Just days since registering ex.Criteria = append(ex.Criteria, f.Criterion{ OrGroup: OrGroup, Key: dateKey, Predicate: f.PredicateGreaterThanOrEquals, Value: promotion.Date, }) case 0, 3, 4, 7: // Posts AND days since registering ex.Criteria = append(ex.Criteria, f.Criterion{ OrGroup: OrGroup, Key: postsKey, Predicate: f.PredicateGreaterThanOrEquals, Value: promotion.Posts, }) ex.Criteria = append(ex.Criteria, f.Criterion{ OrGroup: OrGroup, Key: dateKey, Predicate: f.PredicateGreaterThanOrEquals, Value: promotion.Date, }) case 1, 2, 5, 6: // Posts OR days since registering ex.Criteria = append(ex.Criteria, f.Criterion{ OrGroup: OrGroup, Key: postsKey, Predicate: f.PredicateGreaterThanOrEquals, Value: promotion.Posts, }) OrGroup++ ex.Criteria = append(ex.Criteria, f.Criterion{ OrGroup: OrGroup, Key: dateKey, Predicate: f.PredicateGreaterThanOrEquals, Value: promotion.Date, }) } OrGroup++ } } else { // Export users rows, err = db.Query(` SELECT userid FROM `+config.DB.TablePrefix+`user WHERE usergroupid = ? OR find_in_set(?, membergroupids) <> 0`, id, id, ) if err != nil { return err } defer rows.Close() ids := []f.ID{} for rows.Next() { id := f.ID{} err = rows.Scan(&id.ID) if err != nil { return err } ids = append(ids, id) } err = rows.Err() if err != nil { return err } rows.Close() ex.Users = ids } err = writeFile(filename, ex) if err != nil { return err } addExportFile(ex.ID, filename) return nil }
func exportForum(id int64) error { // Split the filename and ensure the directory exists path, name := splitFilename(strconv.FormatInt(id, 10)) path = config.Export.OutputDirectory + f.ForumsPath + path if !fileExists(path) { err := mkDirAll(path) if err != nil { return err } } filename := fmt.Sprintf("%s/%s.json", path, name) // Don't export if we've exported already if fileExists(filename) { return nil } vb := vbForum{} err := db.QueryRow(` SELECT forumid ,title ,description ,options ,displayorder FROM `+config.DB.TablePrefix+`forum WHERE forumid = ?`, id, ).Scan( &vb.ForumID, &vb.Title, &vb.Description, &vb.Options, &vb.DisplayOrder, ) if err != nil { return err } ex := f.Forum{} ex.ID = vb.ForumID ex.Name = vb.Title ex.Text = vb.Description ex.DisplayOrder = vb.DisplayOrder // vBulletin forums are all created by the admin var adminId int64 err = db.QueryRow(` SELECT MIN(userid) FROM ` + config.DB.TablePrefix + `user WHERE usergroupid = 6`, ).Scan( &adminId, ) if err != nil { return err } ex.Author = adminId // From vBulletin includes/xml/bitfield_vbulletin.xml // <group name="forumoptions"> // <bitfield name="active">1</bitfield> // <bitfield name="allowposting">2</bitfield> // <bitfield name="cancontainthreads">4</bitfield> // <bitfield name="moderatenewpost">8</bitfield> // <bitfield name="moderatenewthread">16</bitfield> // <bitfield name="moderateattach">32</bitfield> // <bitfield name="allowbbcode">64</bitfield> // <bitfield name="allowimages">128</bitfield> // <bitfield name="allowhtml">256</bitfield> // <bitfield name="allowsmilies">512</bitfield> // <bitfield name="allowicons">1024</bitfield> // <bitfield name="allowratings">2048</bitfield> // <bitfield name="countposts">4096</bitfield> // <bitfield name="canhavepassword">8192</bitfield> // <bitfield name="indexposts">16384</bitfield> // <bitfield name="styleoverride">32768</bitfield> // <bitfield name="showonforumjump">65536</bitfield> // <bitfield name="prefixrequired">131072</bitfield> // </group> if vb.Options&1 != 1 { // Forum is not active and cannot be accessed, remove from import return nil } ex.Open = vb.Options&2 != 0 ex.Moderated = vb.Options&16 != 0 // Forum moderators rows, err := db.Query(` SELECT userid FROM `+config.DB.TablePrefix+`moderator WHERE forumid = ?`, id, ) if err != nil { return err } defer rows.Close() mods := []f.ID{} for rows.Next() { mod := f.ID{} err = rows.Scan(&mod.ID) if err != nil { return err } mods = append(mods, mod) } err = rows.Err() if err != nil { return err } rows.Close() ex.Moderators = mods // TODO: this is a better query // Forum specific usergroup permissions rows, err = db.Query(` SELECT p.usergroupid ,p.forumpermissions FROM `+config.DB.TablePrefix+`forumpermission p ,`+config.DB.TablePrefix+`forum f WHERE f.forumid = ? AND FIND_IN_SET(p.forumid, f.parentlist) > 0 ORDER BY FIND_IN_SET(p.forumid, f.parentlist) ASC, usergroupid ASC`, id, ) if err != nil { return err } defer rows.Close() usergroups := []f.Role{} seen := make(map[int64]bool) for rows.Next() { var ( usergroupid int64 forumpermissions int64 ) err = rows.Scan( &usergroupid, &forumpermissions, ) if err != nil { return err } // Skip already seen usergroups. This occurs when permissions are // inherited, the lowest level depth determines priority which is // handled by the SQL query ordering by depth if _, ok := seen[usergroupid]; ok { continue } // From vBulletin includes/xml/bitfield_vbulletin.xml // <group name="forumpermissions"> // <bitfield name="canview" group="forum_viewing_permissions" >1</bitfield> // <bitfield name="canviewthreads" group="forum_viewing_permissions" >524288</bitfield> // <bitfield name="canviewothers" group="forum_viewing_permissions" >2</bitfield> // <bitfield name="cansearch" group="forum_searching_permissions">4</bitfield> // <bitfield name="canemail" group="forum_viewing_permissions" >8</bitfield> // <bitfield name="canpostnew" group="post_thread_permissions" >16</bitfield> // <bitfield name="canreplyown" group="post_thread_permissions" >32</bitfield> // <bitfield name="canreplyothers" group="post_thread_permissions" >64</bitfield> // <bitfield name="caneditpost" group="post_thread_permissions" >128</bitfield> // <bitfield name="candeletepost" group="post_thread_permissions" >256</bitfield> // <bitfield name="candeletethread" group="post_thread_permissions" >512</bitfield> // <bitfield name="canopenclose" group="post_thread_permissions" >1024</bitfield> // <bitfield name="canmove" group="post_thread_permissions" >2048</bitfield> // <bitfield name="cangetattachment" group="forum_viewing_permissions" >4096</bitfield> // <bitfield name="canpostattachment" group="attachment_permissions" >8192</bitfield> // <bitfield name="attachlimit" group="attachment_permissions" >1</bitfield> // <bitfield name="canpostpoll" group="poll_permissions" >16384</bitfield> // <bitfield name="canvote" group="poll_permissions" >32768</bitfield> // <bitfield name="canthreadrate" group="post_thread_permissions" >65536</bitfield> // <bitfield name="followforummoderation" group="post_thread_permissions" >131072</bitfield> // <bitfield name="canseedelnotice" group="forum_viewing_permissions" >262144</bitfield> // <bitfield name="cantagown" group="post_thread_permissions" >1048576</bitfield> // <bitfield name="cantagothers" group="post_thread_permissions" >2097152</bitfield> // <bitfield name="candeletetagown" group="post_thread_permissions" >4194304</bitfield> // <bitfield name="canseethumbnails" group="forum_viewing_permissions" >8388608</bitfield> // </group> perms := f.ForumPermissions{} perms.View = forumpermissions&1 != 0 perms.PostNew = forumpermissions&16 != 0 perms.EditOwn = forumpermissions&128 != 0 perms.EditOthers = false perms.DeleteOwn = forumpermissions&256 != 0 perms.DeleteOthers = false perms.CloseOwn = forumpermissions&1024 != 0 perms.OpenOwn = forumpermissions&1024 != 0 ug := f.Role{} ug.ID = usergroupid ug.ForumPermissions = perms usergroups = append(usergroups, ug) seen[usergroupid] = true } err = rows.Err() if err != nil { return err } rows.Close() ex.Usergroups = usergroups err = writeFile(filename, ex) if err != nil { return err } exportedItemsLock.Lock() exportedItems.Files = append(exportedItems.Files, f.DirFile{ ID: ex.ID, Path: strings.Replace(filename, config.Export.OutputDirectory, "", 1), }) exportedItemsLock.Unlock() return nil }