diff --git a/internal/command/command.go b/internal/command/command.go index a866f78..e2549a8 100644 --- a/internal/command/command.go +++ b/internal/command/command.go @@ -94,32 +94,6 @@ func (c *Config) Verify() error { return nil } -// add missing apps to pages at 30 apps per page -func parseMissing(missing []string, pages []database.Page) []database.Page { - if len(missing) > 0 { - for _, chunk := range split(missing, 30) { - p := database.Page{ - Number: len(pages) + 1, - } - - // because you can't assign a []string to an []interface{} we must copy in one at a time - chunkInterface := make([]interface{}, len(chunk)) - for i, v := range chunk { - chunkInterface[i] = v - } - - p.Items = chunkInterface - pages = append(pages, p) - for _, smallerChunk := range split(chunk, 5) { - msg := fmt.Sprintf("adding missing apps to page=%d", p.Number) - utils.DoubleIndent(log.WithField("apps", smallerChunk).Warn)(msg) - } - } - } - - return pages -} - func parsePages(root int, parentMapping map[int][]database.Item) (database.Apps, error) { var apps database.Apps @@ -288,9 +262,8 @@ func DefaultOrg(c *Config) (err error) { } utils.Indent(log.Info)("creating App folders and adding apps to them") - groupID, err = lpad.ApplyConfig(config.Apps, database.ApplicationType, groupID, 1) - if err != nil { - return fmt.Errorf("failed to ApplyConfig: %v", err) + if err := lpad.ApplyConfig(config.Apps, groupID, 1); err != nil { + return fmt.Errorf("failed to DefaultOrg->ApplyConfig: %w", err) } // Re-enable the update triggers @@ -452,11 +425,11 @@ func SaveConfig(c *Config) (err error) { } // LoadConfig will load your launchpad settings from a config file -func LoadConfig(c *Config) error { +func LoadConfig(c *Config) (err error) { var lpad database.LaunchPad // Read in Config file - config, err := database.LoadConfig(c.File) + lpad.Config, err = database.LoadConfig(c.File) if err != nil { return fmt.Errorf("failed to load config file: %v", err) } @@ -538,14 +511,13 @@ func LoadConfig(c *Config) error { ///////////////////////////////////////////////////////////////////// // Place Apps /////////////////////////////////////////////////////// - if err := lpad.GetMissing(&config.Apps, database.ApplicationType); err != nil { + if err := lpad.GetMissing(&lpad.Config.Apps, database.ApplicationType); err != nil { return fmt.Errorf("failed to GetMissing=>Apps: %v", err) } utils.Indent(log.Info)("creating App folders and adding apps to them") - groupID, err = lpad.ApplyConfig(config.Apps, database.ApplicationType, groupID, 1) - if err != nil { - return fmt.Errorf("failed to ApplyConfig=>Apps: %v", err) + if err := lpad.ApplyConfig(lpad.Config.Apps, groupID, 1); err != nil { + return fmt.Errorf("failed to LoadConfig->ApplyConfig: %w", err) } // Re-enable the update triggers @@ -553,16 +525,20 @@ func LoadConfig(c *Config) error { return fmt.Errorf("failed to EnableTriggers: %v", err) } - if len(config.Desktop.Image) > 0 { - utils.Indent(log.WithField("image", config.Desktop.Image).Info)("setting desktop background image") - desktop.SetDesktopImage(config.Desktop.Image) - } - if err := restartDock(); err != nil { return fmt.Errorf("failed to restart dock: %w", err) } - if len(config.Dock.Apps) > 0 || len(config.Dock.Others) > 0 { + if err := lpad.FixOther(); err != nil { + return fmt.Errorf("failed to fix Other folder: %w", err) + } + + if len(lpad.Config.Desktop.Image) > 0 { + utils.Indent(log.WithField("image", lpad.Config.Desktop.Image).Info)("setting desktop background image") + desktop.SetDesktopImage(lpad.Config.Desktop.Image) + } + + if len(lpad.Config.Dock.Apps) > 0 || len(lpad.Config.Dock.Others) > 0 { utils.Indent(log.Info)("setting dock apps") dPlist, err := dock.LoadDockPlist() if err != nil { @@ -571,19 +547,19 @@ func LoadConfig(c *Config) error { if len(dPlist.PersistentApps) > 0 { dPlist.PersistentApps = nil // remove all apps from dock } - for _, app := range config.Dock.Apps { + for _, app := range lpad.Config.Dock.Apps { utils.DoubleIndent(log.WithField("app", app).Info)("adding to dock") dPlist.AddApp(app) } if len(dPlist.PersistentOthers) > 0 { dPlist.PersistentOthers = nil // remove all folders from dock } - for _, other := range config.Dock.Others { + for _, other := range lpad.Config.Dock.Others { utils.DoubleIndent(log.WithField("other", other).Info)("adding to dock") dPlist.AddOther(other) } - if config.Dock.Settings != nil { - if err := dPlist.ApplySettings(*config.Dock.Settings); err != nil { + if lpad.Config.Dock.Settings != nil { + if err := dPlist.ApplySettings(*lpad.Config.Dock.Settings); err != nil { return fmt.Errorf("failed to apply dock settings: %w", err) } } diff --git a/internal/database/config.go b/internal/database/config.go index bf38e52..4ac5608 100644 --- a/internal/database/config.go +++ b/internal/database/config.go @@ -19,6 +19,31 @@ type Config struct { Desktop Desktop `yaml:"desktop" json:"desktop,omitempty" mapstructure:"desktop"` } +// GetFolderContainingApp returns the folder name that contains the app +func (c Config) GetFolderContainingApp(app string) (string, error) { + for _, page := range c.Apps.Pages { + for _, item := range page.Items { + switch item.(type) { + case string: + continue + default: + var folder AppFolder + if err := mapstructure.Decode(item, &folder); err != nil { + return "", errors.Wrap(err, "mapstructure unable to decode config folder") + } + for _, page := range folder.Pages { + for _, item := range page.Items { + if item == app { + return folder.Name, nil + } + } + } + } + } + } + return "", fmt.Errorf("unable to find folder containing app %s", app) +} + // Apps is the launchpad apps config object type Apps struct { Pages []Page `yaml:"pages" json:"pages,omitempty"` diff --git a/internal/database/database.go b/internal/database/database.go index 4743a04..45d5c1f 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -17,11 +17,6 @@ import ( // GetMissing returns a list of the rest of the apps not in the config func (lp *LaunchPad) GetMissing(apps *Apps, appType int) error { - var ( - dbApps []string - configApps []string - ) - // get all apps from database switch appType { case ApplicationType: @@ -35,38 +30,39 @@ func (lp *LaunchPad) GetMissing(apps *Apps, appType int) error { return fmt.Errorf("query all apps failed: %w", err) } for _, app := range apps { - dbApps = append(dbApps, app.Title) + lp.dbApps = append(lp.dbApps, app.Title) } default: return fmt.Errorf("GetMissing: unsupported app type: %d", appType) } - sort.Strings(dbApps) + sort.Strings(lp.dbApps) // get all apps from config file for _, page := range apps.Pages { for _, item := range page.Items { switch item.(type) { case string: - configApps = append(configApps, item.(string)) + lp.confApps = append(lp.confApps, item.(string)) default: var folder AppFolder if err := mapstructure.Decode(item, &folder); err != nil { return fmt.Errorf("mapstructure unable to decode config folder: %w", err) } + lp.confFolders = append(lp.confFolders, folder.Name) for _, fpage := range folder.Pages { for _, fitem := range fpage.Items { - configApps = append(configApps, fitem) + lp.confApps = append(lp.confApps, fitem) } } } } } - sort.Strings(configApps) + sort.Strings(lp.confApps) - for _, app := range dbApps { - if !slices.Contains(configApps, app) { + for _, app := range lp.dbApps { + if !slices.Contains(lp.confApps, app) { utils.DoubleIndent(log.WithField("app", app).Warn)("found installed apps that are not in supplied config") if len(apps.Pages[len(apps.Pages)-1].Items) < 35 { apps.Pages[len(apps.Pages)-1].Items = append(apps.Pages[len(apps.Pages)-1].Items, app) @@ -85,7 +81,7 @@ func (lp *LaunchPad) GetMissing(apps *Apps, appType int) error { for iidx, item := range page.Items { switch item.(type) { case string: - if !slices.Contains(dbApps, item.(string)) { + if !slices.Contains(lp.dbApps, item.(string)) { utils.DoubleIndent(log.WithField("app", item.(string)).Warn)("found app in config that are is not on system") apps.Pages[idx].Items = append(apps.Pages[idx].Items[:iidx], apps.Pages[idx].Items[iidx+1:]...) } @@ -96,7 +92,7 @@ func (lp *LaunchPad) GetMissing(apps *Apps, appType int) error { } for fpIdx, fpage := range folder.Pages { for fpiIdx, fitem := range fpage.Items { - if !slices.Contains(dbApps, fitem) { + if !slices.Contains(lp.dbApps, fitem) { utils.DoubleIndent(log.WithField("app", fitem).Warn)("found app in config that are is not on system") apps.Pages[idx].Items[iidx].(map[string]any)["pages"].([]any)[fpIdx].(map[string]any)["items"] = append( apps.Pages[idx].Items[iidx].(map[string]any)["pages"].([]any)[fpIdx].(map[string]any)["items"].([]any)[:fpiIdx], @@ -114,34 +110,54 @@ func (lp *LaunchPad) GetMissing(apps *Apps, appType int) error { // ClearGroups clears out items related to groups func (lp *LaunchPad) ClearGroups() error { utils.Indent(log.Info)("clear out groups") - var items []Item - return lp.DB.Where("type in (?)", []int{RootType, FolderRootType, PageType}).Delete(&items).Error + if err := lp.DB.Where("type in (?)", []int{RootType, FolderRootType, PageType}).Delete(&items).Error; err != nil { + return fmt.Errorf("delete items associted with groups failed: %w", err) + } + // return lp.DB.Exec("DELETE FROM groups;").Error + return nil +} + +// FlattenApps sets all the apps to the root page +func (lp *LaunchPad) FlattenApps() error { + var apps []App + + if err := lp.DB.Find(&apps).Error; err != nil { + return fmt.Errorf("query all apps failed: %w", err) + } + + lp.DisableTriggers() + + utils.Indent(log.Info)("flattening out apps") + for idx, app := range apps { + if err := lp.updateItem(app.Title, ApplicationType, lp.rootPage, idx); err != nil { + return fmt.Errorf("failed to update app '%s': %w", app.Title, err) + } + } + + lp.EnableTriggers() + + return nil } // AddRootsAndHoldingPages adds back in the RootPage and HoldingPage defaults func (lp *LaunchPad) AddRootsAndHoldingPages() error { - utils.Indent(log.Info)("add root and holding pages") items := []Item{ {ID: 1, UUID: "ROOTPAGE", Type: RootType, ParentID: 0, Ordering: 0}, {ID: 2, UUID: "HOLDINGPAGE", Type: PageType, ParentID: 1, Ordering: 0}, - {ID: 3, UUID: "ROOTPAGE_DB", Type: RootType, ParentID: 0, Ordering: 0}, + // {ID: 3, UUID: "ROOTPAGE_DB", Type: PageType, ParentID: 0, Ordering: 0}, {ID: 4, UUID: "HOLDINGPAGE_DB", Type: PageType, ParentID: 3, Ordering: 0}, {ID: 5, UUID: "ROOTPAGE_VERS", Type: RootType, ParentID: 0, Ordering: 0}, {ID: 6, UUID: "HOLDINGPAGE_VERS", Type: PageType, ParentID: 5, Ordering: 0}, } + utils.Indent(log.Info)("add root and holding pages") for _, item := range items { - group := Group{ID: item.ID} - - // if !lp.DB.NewRecord(item) { - // log.Error("create new record failed") - // } if err := lp.DB.Create(&item).Error; err != nil { return errors.Wrap(err, "db insert item failed") } - if err := lp.DB.Create(&group).Error; err != nil { + if err := lp.DB.Create(&Group{ID: item.ID}).Error; err != nil { return errors.Wrap(err, "db insert group failed") } } @@ -150,41 +166,34 @@ func (lp *LaunchPad) AddRootsAndHoldingPages() error { } // createNewPage creates a new page -func (lp *LaunchPad) createNewPage(pageNumber, groupID, pageParentID int) error { +func (lp *LaunchPad) createNewPage(rowID, pageParentID, pageNumber int) error { item := Item{ - ID: groupID, + ID: rowID, UUID: uuid.New().String(), Flags: 2, Type: PageType, ParentID: pageParentID, - Ordering: pageNumber, // TODO: check if I should use 0 base index or 1 (what I'm doing now) + Ordering: pageNumber, } - // if !lp.DB.NewRecord(item) { - // utils.DoubleIndent(log.WithField("item", item).Debug)("createNewPage - create new item record failed") - // } if err := lp.DB.Create(&item).Error; err != nil { - return errors.Wrap(err, "createNewPage") + return fmt.Errorf("failed to create page item with ID=%d: %w", rowID, err) } - group := Group{ID: groupID} // omitting fields makes them null - - // if !lp.DB.NewRecord(group) { - // utils.Indent(log.WithField("group", group).Debug)("createNewPage - create new group record failed") - // } - if err := lp.DB.Create(&group).Error; err != nil { - return errors.Wrap(err, "createNewPage") + utils.DoubleIndent(log.WithField("number", pageNumber).Info)("page added") + if err := lp.DB.Create(&Group{ID: rowID}).Error; err != nil { + return fmt.Errorf("failed to create group for page with ID=%d: %w", rowID, err) } return nil } // createNewFolder creates a new app folder -func (lp *LaunchPad) createNewFolder(folderName string, folderNumber, groupID, folderParentID int) error { +func (lp *LaunchPad) createNewFolder(folderName string, rowID, folderParentID, folderNumber int) error { item := Item{ - ID: groupID, + ID: rowID, UUID: uuid.New().String(), Flags: 0, Type: FolderRootType, @@ -192,35 +201,30 @@ func (lp *LaunchPad) createNewFolder(folderName string, folderNumber, groupID, f Ordering: folderNumber, } - // if !lp.DB.NewRecord(item) { - // utils.DoubleIndent(log.WithField("item", item).Debug)("createNewFolder - create new item record failed") - // } - if err := lp.DB.Create(&item).Error; err != nil { - return errors.Wrap(err, "createNewFolder") + if folderName == "Utilities" { + item.Flags = 1 } - group := Group{ - ID: groupID, - Title: folderName, + if err := lp.DB.Create(&item).Error; err != nil { + return fmt.Errorf("failed to create folder '%s' item with ID=%d: %w", folderName, rowID, err) } - utils.DoubleIndent(log.WithField("group", group.Title).Info)("folder added") - - // if !lp.DB.NewRecord(group) { - // utils.Indent(log.WithField("group", group).Debug)("createNewFolder - create new group record failed") - // } - if err := lp.DB.Create(&group).Error; err != nil { - return errors.Wrap(err, "createNewFolder") + utils.DoubleIndent(log.WithField("group", folderName).Info)("folder added") + if err := lp.DB.Create(&Group{ + ID: rowID, + Title: folderName, + }).Error; err != nil { + return fmt.Errorf("failed to create group for folder '%s' with ID=%d: %w", folderName, rowID, err) } return nil } // createNewFolderPage creates a new folder page -func (lp *LaunchPad) createNewFolderPage(folderPageNumber, groupID, folderPageParentID int) error { +func (lp *LaunchPad) createNewFolderPage(rowID, folderPageParentID, folderPageNumber int) error { item := Item{ - ID: groupID, + ID: rowID, UUID: uuid.New().String(), Flags: 2, Type: PageType, @@ -228,48 +232,32 @@ func (lp *LaunchPad) createNewFolderPage(folderPageNumber, groupID, folderPagePa Ordering: folderPageNumber, } - // if !lp.DB.NewRecord(item) { - // utils.DoubleIndent(log.WithField("item", item).Debug)("createNewFolderPage - create new item record failed") - // } if err := lp.DB.Create(&item).Error; err != nil { - return errors.Wrap(err, "createNewFolderPage") + return fmt.Errorf("failed to create folder page item with ID=%d: %w", rowID, err) } - - group := Group{ID: groupID} - // if !lp.DB.NewRecord(group) { - // utils.Indent(log.WithField("group", group).Debug)("createNewFolderPage - create new group record failed") - // } - if err := lp.DB.Create(&group).Error; err != nil { - return errors.Wrap(err, "createNewFolderPage") + utils.DoubleIndent(log.WithField("number", folderPageNumber).Info)("folder page added") + if err := lp.DB.Create(&Group{ID: rowID}).Error; err != nil { + return fmt.Errorf("failed to create group for folder page with ID=%d: %w", rowID, err) } return nil } // updateItem will add the apps/widgets to the correct page/folder -func (lp *LaunchPad) updateItem(item string, ordering, groupID, itemType int) error { - - var ( - i Item - a App - w Widget - ) +func (lp *LaunchPad) updateItem(item string, itemType, parentID, ordering int) error { - i = Item{} - a = App{} - w = Widget{} + i := Item{} + a := App{} + w := Widget{} switch itemType { case ApplicationType: - - if result := lp.DB.Where("title = ?", item).First(&a); result.Error != nil && errors.Is(result.Error, gorm.ErrRecordNotFound) { - utils.DoubleIndent(log.WithField("app", item).Warn)("app not installed. SKIPPING...") - return nil + if err := lp.DB.Where("title = ?", item).First(&a).Error; err != nil { + return fmt.Errorf("app query failed for '%s': %w", item, err) } if err := lp.DB.Where("rowid = ?", a.ID).First(&i).Error; err != nil { - return errors.Wrap(err, "item query failed for app: "+item) + return fmt.Errorf("item query failed for app ID %d: %w", a.ID, err) } - lp.DB.Model(&i).Association("App").Find(&i.App) case WidgetType: if result := lp.DB.Where("title = ?", item).First(&w); result.Error != nil && errors.Is(result.Error, gorm.ErrRecordNotFound) { @@ -277,12 +265,11 @@ func (lp *LaunchPad) updateItem(item string, ordering, groupID, itemType int) er return nil } if err := lp.DB.Where("rowid = ?", w.ID).First(&i).Error; err != nil { - return errors.Wrap(err, "item query failed for widget: "+item) + return fmt.Errorf("item query failed for wiget ID %d: %w", a.ID, err) } - lp.DB.Model(&i).Association("Widget").Find(&i.Widget) default: - utils.DoubleIndent(log.WithField("type", itemType).Error)("bad type") + return fmt.Errorf("failed to update item: unknown item type: %d", itemType) } newItem := Item{ @@ -290,25 +277,25 @@ func (lp *LaunchPad) updateItem(item string, ordering, groupID, itemType int) er UUID: i.UUID, Flags: i.Flags, Type: itemType, - ParentID: groupID, + ParentID: parentID, Ordering: ordering, } - // if !lp.DB.NewRecord(newItem) { - // utils.DoubleIndent(log.WithField("item", newItem).Debug)("createItems - create new item record failed") - // } return lp.DB.Save(&newItem).Error } -// ApplyConfig places all the launchpad apps -func (lp *LaunchPad) ApplyConfig(config Apps, itemType, groupID, rootParentID int) (int, error) { +func (lp *LaunchPad) ApplyConfig(config Apps, groupID, rootParentID int) error { for _, page := range config.Pages { - // create a new page groupID++ - err := lp.createNewPage(page.Number, groupID, rootParentID) + // create a new page + err := lp.createNewPage(groupID, rootParentID, page.Number) if err != nil { - return groupID, errors.Wrap(err, "createNewPage") + return errors.Wrap(err, "createNewPage") + } + + if page.Number == 1 { + lp.rootPage = groupID } pageParentID := groupID @@ -317,20 +304,20 @@ func (lp *LaunchPad) ApplyConfig(config Apps, itemType, groupID, rootParentID in switch item.(type) { case string: // add a flat item - if err := lp.updateItem(item.(string), idx, pageParentID, itemType); err != nil { - return groupID, errors.Wrap(err, "updateItem") + if err := lp.updateItem(item.(string), ApplicationType, pageParentID, idx); err != nil { + return errors.Wrap(err, "updateItem") } default: var folder AppFolder if err := mapstructure.Decode(item, &folder); err != nil { - return groupID, errors.Wrap(err, "mapstructure unable to decode config folder") + return errors.Wrap(err, "mapstructure unable to decode config folder") } // create a new folder groupID++ - err := lp.createNewFolder(folder.Name, idx, groupID, pageParentID) + err := lp.createNewFolder(folder.Name, groupID, pageParentID, idx) if err != nil { - return groupID, errors.Wrap(err, "createNewFolder") + return errors.Wrap(err, "createNewFolder") } folderParentID := groupID @@ -338,14 +325,14 @@ func (lp *LaunchPad) ApplyConfig(config Apps, itemType, groupID, rootParentID in for _, fpage := range folder.Pages { // create a new folder page groupID++ - if err := lp.createNewFolderPage(fpage.Number, groupID, folderParentID); err != nil { - return groupID, errors.Wrap(err, "createNewFolderPage") + if err := lp.createNewFolderPage(groupID, folderParentID, fpage.Number); err != nil { + return errors.Wrap(err, "createNewFolderPage") } // add all folder page items for fidx, fitem := range fpage.Items { - if err := lp.updateItem(fitem, fidx, groupID, itemType); err != nil { - return groupID, errors.Wrap(err, "updateItem") + if err := lp.updateItem(fitem, ApplicationType, groupID, fidx); err != nil { + return errors.Wrap(err, "updateItem") } } } @@ -353,33 +340,214 @@ func (lp *LaunchPad) ApplyConfig(config Apps, itemType, groupID, rootParentID in } } - return groupID, nil + return nil +} + +// // ApplyConfig places all the launchpad apps +// func (lp *LaunchPad) ApplyConfig(config Apps, startingID, rootParentID int) error { + +// var ( +// rowID int +// pageID int +// pageParentID int +// folderID int +// folderParentID int +// folderPageID int +// ) + +// rowID = startingID + +// for _, page := range config.Pages { +// rowID++ +// pageID = rowID + +// // create a new page +// if err := lp.createNewPage(pageID, rootParentID, page.Number); err != nil { +// return fmt.Errorf("createNewPage failed: %w", err) +// } + +// if page.Number == 1 { // flatten out apps to first page +// lp.rootPage = pageID +// // lp.FlattenApps() +// } + +// pageParentID = pageID + +// for idx, item := range page.Items { +// switch item.(type) { +// case string: +// // add a folder-less app to the current page +// if err := lp.updateItem(item.(string), ApplicationType, pageParentID, idx); err != nil { +// return fmt.Errorf("failed to update folder-less app '%s': %w", item, err) +// } +// default: +// var folder AppFolder +// if err := mapstructure.Decode(item, &folder); err != nil { +// return fmt.Errorf("mapstructure unable to decode config folder: %w", err) +// } + +// // create a new folder +// rowID++ +// folderID = rowID +// if err := lp.createNewFolder(folder.Name, folderID, pageParentID, idx); err != nil { +// return fmt.Errorf("failed to create folder '%s' with ID=%d: %w", folder.Name, folderID, err) +// } + +// folderParentID = folderID + +// for fpidx, fpage := range folder.Pages { +// rowID++ +// folderPageID = rowID +// // create a new folder page +// if err := lp.createNewFolderPage(folderPageID, folderParentID, fpidx); err != nil { +// return fmt.Errorf("failed to create page #%d for folder '%s' failed: %w", fpage.Number, folder.Name, err) +// } +// // add all folder page items +// for fpiIDX, fpItem := range fpage.Items { +// if err := lp.updateItem(fpItem, ApplicationType, folderPageID, fpiIDX); err != nil { +// return fmt.Errorf("failed to update folder app '%s': %w", fpItem, err) +// } +// } +// } +// } +// } +// } + +// return nil +// } + +func (lp *LaunchPad) addToFolder(appName, folderName string) error { + var a App + if err := lp.DB.Where("title = ?", appName).First(&a).Error; err != nil { + return fmt.Errorf("app query failed for '%s': %w", appName, err) + } + var app Item + if err := lp.DB.Where("rowid = ?", a.ID).First(&app).Error; err != nil { + return fmt.Errorf("item query failed for app ID %d: %w", a.ID, err) + } + + var g Group + if err := lp.DB.Where("title = ?", folderName).First(&g).Error; err != nil { + return fmt.Errorf("group query failed for '%s': %w", folderName, err) + } + var folder Item + if err := lp.DB.Where("rowid = ?", g.ID).First(&folder).Error; err != nil { + return fmt.Errorf("item query failed for folder ID %d: %w", a.ID, err) + } + var pages []Item + if err := lp.DB.Where("parent_id = ?", folder.ID).Find(&pages).Error; err != nil { + return fmt.Errorf("failed to find pages for folder '%s': %w", folderName, err) + } + if len(pages) == 0 { + return fmt.Errorf("folder '%s' has no pages", folderName) + } + var folderApps []Item + if err := lp.DB.Where("parent_id = ?", pages[0].ID).Find(&folderApps).Error; err != nil { + return fmt.Errorf("failed to find apps for page '%d': %w", pages[0].ID, err) + } + + if err := lp.updateItem(appName, ApplicationType, pages[0].ID, len(folderApps)); err != nil { + return fmt.Errorf("failed to add app '%s' to folder '%s': %w", appName, folderName, err) + } + + return nil +} + +// FixOther moves all apps in the 'Other' group to the root page +func (lp *LaunchPad) FixOther() error { + if slices.Contains(lp.confFolders, "Other") { // config contain Other folder (no need to fix) + return nil + } + + var other Group + if err := lp.DB.Where("title = ?", "Other").Find(&other).Error; err != nil { + return fmt.Errorf("failed to find group 'Other': %w", err) + } + if other.Title != "Other" { + return fmt.Errorf("group 'Other' not found") + } + + var pages []Item + if err := lp.DB.Where("parent_id = ?", other.ID).Find(&pages).Error; err != nil { + return fmt.Errorf("failed to find pages for group 'Other': %w", err) + } + + var apps []App + for _, page := range pages { + var pageItems []Item + if err := lp.DB.Where("parent_id = ?", page.ID).Find(&pageItems).Error; err != nil { + return fmt.Errorf("failed to find apps for page '%s': %w", page.UUID, err) + } + for _, pageItem := range pageItems { + lp.DB.Model(&pageItem).Association("App").Find(&pageItem.App) + apps = append(apps, pageItem.App) + } + } + + // move apps to root page + for _, app := range apps { + utils.DoubleIndent(log.WithField("app", app.Title).Warn)("moving app from Other folder") + if cfolder, err := lp.Config.GetFolderContainingApp(app.Title); err == nil { + if err := lp.addToFolder(app.Title, cfolder); err != nil { // add to folder it SHOULD have been in + return err + } + } else { + if err := lp.updateItem(app.Title, ApplicationType, lp.rootPage, -1); err != nil { // add to end of root page + return fmt.Errorf("failed to move app '%s' from Other to root: %w", app.Title, err) + } + } + + } + + // remove all traces of Other + for _, page := range pages { + if err := lp.DB.Delete(&page).Error; err != nil { + return fmt.Errorf("failed to delete page '%s': %w", page.UUID, err) + } + if err := lp.DB.Delete(&Group{ID: page.ID}).Error; err != nil { + return fmt.Errorf("failed to delete group for page '%s': %w", page.UUID, err) + } + } + if err := lp.DB.Delete(&other).Error; err != nil { + return fmt.Errorf("failed to delete group 'Other': %w", err) + } + if err := lp.DB.Delete(&Item{ID: other.ID}).Error; err != nil { + return fmt.Errorf("failed to delete item for 'Other': %w", err) + } + + return nil } // EnableTriggers enables item update triggers func (lp *LaunchPad) EnableTriggers() error { - utils.Indent(log.Info)("enabling SQL update triggers") - - if err := lp.DB.Exec("UPDATE dbinfo SET value = 0 WHERE key = 'ignore_items_update_triggers';").Error; err != nil { + if err := lp.DB.Exec("UPDATE dbinfo SET value=0 WHERE key='ignore_items_update_triggers';").Error; err != nil { return errors.Wrap(err, "counld not update `ignore_items_update_triggers` to 0") } - return nil } // DisableTriggers disables item update triggers func (lp *LaunchPad) DisableTriggers() error { - utils.Indent(log.Info)("disabling SQL update triggers") - - if err := lp.DB.Exec("UPDATE dbinfo SET value = 1 WHERE key = 'ignore_items_update_triggers';").Error; err != nil { + if err := lp.DB.Exec("UPDATE dbinfo SET value=1 WHERE key='ignore_items_update_triggers';").Error; err != nil { return errors.Wrap(err, "counld not update `ignore_items_update_triggers` to 1") } - return nil } +// TriggersDisabled returns true if triggers are disabled +func (lp *LaunchPad) TriggersDisabled() bool { + var dbinfo DBInfo + if err := lp.DB.Where("key in (?)", []string{"ignore_items_update_triggers"}).Find(&dbinfo).Error; err != nil { + log.WithError(err).Error("dbinfo query failed") + } + if dbinfo.Value == "1" { + return true + } + return false +} + // GetMaxAppID returns the maximum App ItemID func (lp *LaunchPad) GetMaxAppID() int { var apps []App diff --git a/internal/database/model.go b/internal/database/model.go index b3b6c0e..0533611 100644 --- a/internal/database/model.go +++ b/internal/database/model.go @@ -19,13 +19,12 @@ type LaunchPad struct { File string Folder string - rootPage int - missingApps []string - configlessApps []string -} + Config Config -func (lp *LaunchPad) Missing() []string { - return lp.missingApps + rootPage int + dbApps []string + confApps []string + confFolders []string } // App CREATE TABLE apps (item_id INTEGER PRIMARY KEY, title VARCHAR, bundleid VARCHAR, storeid VARCHAR,category_id INTEGER, moddate REAL, bookmark BLOB)