Search in sources :

Example 31 with Template

use of com.ichi2.libanki.template.Template in project Anki-Android by ankidroid.

the class CardContentProvider method insert.

@Override
public Uri insert(@NonNull Uri uri, ContentValues values) {
    if (!hasReadWritePermission() && shouldEnforceQueryOrInsertSecurity()) {
        throwSecurityException("insert", uri);
    }
    Collection col = CollectionHelper.getInstance().getCol(mContext);
    if (col == null) {
        throw new IllegalStateException(COL_NULL_ERROR_MSG);
    }
    col.log(getLogMessage("insert", uri));
    // Find out what data the user is requesting
    int match = sUriMatcher.match(uri);
    switch(match) {
        case NOTES:
            {
                /* Insert new note with specified fields and tags
                 */
                Long modelId = values.getAsLong(FlashCardsContract.Note.MID);
                String flds = values.getAsString(FlashCardsContract.Note.FLDS);
                String tags = values.getAsString(FlashCardsContract.Note.TAGS);
                Models.AllowEmpty allowEmpty = Models.AllowEmpty.fromBoolean(values.getAsBoolean(FlashCardsContract.Note.ALLOW_EMPTY));
                // Create empty note
                com.ichi2.libanki.Note newNote = new com.ichi2.libanki.Note(col, col.getModels().get(modelId));
                // Set fields
                String[] fldsArray = Utils.splitFields(flds);
                // Check that correct number of flds specified
                if (fldsArray.length != newNote.getFields().length) {
                    throw new IllegalArgumentException("Incorrect flds argument : " + flds);
                }
                for (int idx = 0; idx < fldsArray.length; idx++) {
                    newNote.setField(idx, fldsArray[idx]);
                }
                // Set tags
                if (tags != null) {
                    newNote.setTagsFromStr(tags);
                }
                // Add to collection
                col.addNote(newNote, allowEmpty);
                col.save();
                return Uri.withAppendedPath(FlashCardsContract.Note.CONTENT_URI, Long.toString(newNote.getId()));
            }
        case NOTES_ID:
            // Note ID is generated automatically by libanki
            throw new IllegalArgumentException("Not possible to insert note with specific ID");
        case NOTES_ID_CARDS:
        case NOTES_ID_CARDS_ORD:
            // Cards are generated automatically by libanki
            throw new IllegalArgumentException("Not possible to insert cards directly (only through NOTES)");
        case MODELS:
            // Get input arguments
            String modelName = values.getAsString(FlashCardsContract.Model.NAME);
            String css = values.getAsString(FlashCardsContract.Model.CSS);
            Long did = values.getAsLong(FlashCardsContract.Model.DECK_ID);
            String fieldNames = values.getAsString(FlashCardsContract.Model.FIELD_NAMES);
            Integer numCards = values.getAsInteger(FlashCardsContract.Model.NUM_CARDS);
            Integer sortf = values.getAsInteger(FlashCardsContract.Model.SORT_FIELD_INDEX);
            Integer type = values.getAsInteger(FlashCardsContract.Model.TYPE);
            String latexPost = values.getAsString(FlashCardsContract.Model.LATEX_POST);
            String latexPre = values.getAsString(FlashCardsContract.Model.LATEX_PRE);
            // Throw exception if required fields empty
            if (modelName == null || fieldNames == null || numCards == null) {
                throw new IllegalArgumentException("Model name, field_names, and num_cards can't be empty");
            }
            if (did != null && col.getDecks().isDyn(did)) {
                throw new IllegalArgumentException("Cannot set a filtered deck as default deck for a model");
            }
            // Create a new model
            ModelManager mm = col.getModels();
            Model newModel = mm.newModel(modelName);
            try {
                // Add the fields
                String[] allFields = Utils.splitFields(fieldNames);
                for (String f : allFields) {
                    mm.addFieldInNewModel(newModel, mm.newField(f));
                }
                // Add some empty card templates
                for (int idx = 0; idx < numCards; idx++) {
                    String card_name = mContext.getResources().getString(R.string.card_n_name, idx + 1);
                    JSONObject t = Models.newTemplate(card_name);
                    t.put("qfmt", String.format("{{%s}}", allFields[0]));
                    String answerField = allFields[0];
                    if (allFields.length > 1) {
                        answerField = allFields[1];
                    }
                    t.put("afmt", String.format("{{FrontSide}}\\n\\n<hr id=answer>\\n\\n{{%s}}", answerField));
                    mm.addTemplateInNewModel(newModel, t);
                }
                // Add the CSS if specified
                if (css != null) {
                    newModel.put("css", css);
                }
                // Add the did if specified
                if (did != null) {
                    newModel.put("did", did);
                }
                if (sortf != null && sortf < allFields.length) {
                    newModel.put("sortf", sortf);
                }
                if (type != null) {
                    newModel.put("type", type);
                }
                if (latexPost != null) {
                    newModel.put("latexPost", latexPost);
                }
                if (latexPre != null) {
                    newModel.put("latexPre", latexPre);
                }
                // Add the model to collection (from this point on edits will require a full-sync)
                mm.add(newModel);
                col.save();
                // Get the mid and return a URI
                String mid = Long.toString(newModel.getLong("id"));
                return Uri.withAppendedPath(FlashCardsContract.Model.CONTENT_URI, mid);
            } catch (JSONException e) {
                Timber.e(e, "Could not set a field of new model %s", modelName);
                return null;
            }
        case MODELS_ID:
            // Model ID is generated automatically by libanki
            throw new IllegalArgumentException("Not possible to insert model with specific ID");
        case MODELS_ID_TEMPLATES:
            {
                ModelManager models = col.getModels();
                Long mid = getModelIdFromUri(uri, col);
                Model existingModel = models.get(mid);
                if (existingModel == null) {
                    throw new IllegalArgumentException("model missing: " + mid);
                }
                String name = values.getAsString(CardTemplate.NAME);
                String qfmt = values.getAsString(CardTemplate.QUESTION_FORMAT);
                String afmt = values.getAsString(CardTemplate.ANSWER_FORMAT);
                String bqfmt = values.getAsString(CardTemplate.BROWSER_QUESTION_FORMAT);
                String bafmt = values.getAsString(CardTemplate.BROWSER_ANSWER_FORMAT);
                try {
                    JSONObject t = Models.newTemplate(name);
                    t.put("qfmt", qfmt);
                    t.put("afmt", afmt);
                    t.put("bqfmt", bqfmt);
                    t.put("bafmt", bafmt);
                    models.addTemplate(existingModel, t);
                    models.save(existingModel);
                    col.save();
                    return ContentUris.withAppendedId(uri, t.getInt("ord"));
                } catch (ConfirmModSchemaException e) {
                    throw new IllegalArgumentException("Unable to add template without user requesting/accepting full-sync", e);
                } catch (JSONException e) {
                    throw new IllegalArgumentException("Unable to get ord from new template", e);
                }
            }
        case MODELS_ID_TEMPLATES_ID:
            throw new IllegalArgumentException("Not possible to insert template with specific ORD");
        case MODELS_ID_FIELDS:
            {
                ModelManager models = col.getModels();
                long mid = getModelIdFromUri(uri, col);
                Model existingModel = models.get(mid);
                if (existingModel == null) {
                    throw new IllegalArgumentException("model missing: " + mid);
                }
                String name = values.getAsString(FlashCardsContract.Model.FIELD_NAME);
                if (name == null) {
                    throw new IllegalArgumentException("field name missing for model: " + mid);
                }
                JSONObject field = models.newField(name);
                try {
                    models.addField(existingModel, field);
                    col.save();
                    JSONArray flds = existingModel.getJSONArray("flds");
                    return ContentUris.withAppendedId(uri, flds.length() - 1);
                } catch (ConfirmModSchemaException e) {
                    throw new IllegalArgumentException("Unable to insert field: " + name, e);
                } catch (JSONException e) {
                    throw new IllegalArgumentException("Unable to get newly created field: " + name, e);
                }
            }
        case SCHEDULE:
            // Doesn't make sense to insert an object into the schedule table
            throw new IllegalArgumentException("Not possible to perform insert operation on schedule");
        case DECKS:
            // Insert new deck with specified name
            String deckName = values.getAsString(FlashCardsContract.Deck.DECK_NAME);
            did = col.getDecks().id_for_name(deckName);
            if (did != null) {
                throw new IllegalArgumentException("Deck name already exists: " + deckName);
            }
            if (!Decks.isValidDeckName(deckName)) {
                throw new IllegalArgumentException("Invalid deck name '" + deckName + "'");
            }
            try {
                did = col.getDecks().id(deckName);
            } catch (DeckRenameException filteredSubdeck) {
                throw new IllegalArgumentException(filteredSubdeck.getMessage());
            }
            Deck deck = col.getDecks().get(did);
            if (deck != null) {
                try {
                    String deckDesc = values.getAsString(FlashCardsContract.Deck.DECK_DESC);
                    if (deckDesc != null) {
                        deck.put("desc", deckDesc);
                    }
                } catch (JSONException e) {
                    Timber.e(e, "Could not set a field of new deck %s", deckName);
                    return null;
                }
            }
            col.getDecks().flush();
            return Uri.withAppendedPath(FlashCardsContract.Deck.CONTENT_ALL_URI, Long.toString(did));
        case DECK_SELECTED:
            // Can't have more than one selected deck
            throw new IllegalArgumentException("Selected deck can only be queried and updated");
        case DECKS_ID:
            // Deck ID is generated automatically by libanki
            throw new IllegalArgumentException("Not possible to insert deck with specific ID");
        case MEDIA:
            // contentvalue should have data and preferredFileName values
            return insertMediaFile(values, col);
        default:
            // Unknown URI type
            throw new IllegalArgumentException("uri " + uri + " is not supported");
    }
}
Also used : Note(com.ichi2.libanki.Note) JSONArray(com.ichi2.utils.JSONArray) JSONException(com.ichi2.utils.JSONException) Deck(com.ichi2.libanki.Deck) ModelManager(com.ichi2.libanki.ModelManager) DeckRenameException(com.ichi2.libanki.backend.exception.DeckRenameException) JSONObject(com.ichi2.utils.JSONObject) Note(com.ichi2.libanki.Note) Model(com.ichi2.libanki.Model) Collection(com.ichi2.libanki.Collection) ConfirmModSchemaException(com.ichi2.anki.exception.ConfirmModSchemaException)

Example 32 with Template

use of com.ichi2.libanki.template.Template in project Anki-Android by ankidroid.

the class Models method moveTemplate.

@Override
public void moveTemplate(Model m, JSONObject template, int idx) {
    JSONArray tmpls = m.getJSONArray("tmpls");
    int oldidx = -1;
    ArrayList<JSONObject> l = new ArrayList<>();
    HashMap<Integer, Integer> oldidxs = new HashMap<>();
    for (int i = 0; i < tmpls.length(); ++i) {
        if (tmpls.getJSONObject(i).equals(template)) {
            oldidx = i;
            if (idx == oldidx) {
                return;
            }
        }
        JSONObject t = tmpls.getJSONObject(i);
        oldidxs.put(t.hashCode(), t.getInt("ord"));
        l.add(t);
    }
    l.remove(oldidx);
    l.add(idx, template);
    m.put("tmpls", new JSONArray(l));
    _updateTemplOrds(m);
    // generate change map - We use StringBuilder
    StringBuilder sb = new StringBuilder();
    tmpls = m.getJSONArray("tmpls");
    for (int i = 0; i < tmpls.length(); ++i) {
        JSONObject t = tmpls.getJSONObject(i);
        sb.append("when ord = ").append(oldidxs.get(t.hashCode())).append(" then ").append(t.getInt("ord"));
        if (i != tmpls.length() - 1) {
            sb.append(" ");
        }
    }
    // apply
    save(m);
    mCol.getDb().execute("update cards set ord = (case " + sb + " end),usn=?,mod=? where nid in (select id from notes where mid = ?)", mCol.usn(), mCol.getTime().intTime(), m.getLong("id"));
}
Also used : JSONObject(com.ichi2.utils.JSONObject) HashMap(java.util.HashMap) WeakHashMap(java.util.WeakHashMap) JSONArray(com.ichi2.utils.JSONArray) ArrayList(java.util.ArrayList)

Example 33 with Template

use of com.ichi2.libanki.template.Template in project Anki-Android by ankidroid.

the class Models method _availStandardOrds.

/**
 * Given a joined field string and a standard note type, return available template ordinals
 */
public static ArrayList<Integer> _availStandardOrds(Model m, String[] sfld, List<ParsedNode> nodes, boolean allowEmpty) {
    Set<String> nonEmptyFields = m.nonEmptyFields(sfld);
    ArrayList<Integer> avail = new ArrayList<>(nodes.size());
    for (int i = 0; i < nodes.size(); i++) {
        ParsedNode node = nodes.get(i);
        if (node != null && !node.template_is_empty(nonEmptyFields)) {
            avail.add(i);
        }
    }
    if (allowEmpty && avail.isEmpty()) {
        /* According to anki documentation:
            When adding/importing, if a normal note doesn’t generate any cards, Anki will now add a blank card 1 instead of refusing to add the note. */
        avail.add(0);
    }
    return avail;
}
Also used : ParsedNode(com.ichi2.libanki.template.ParsedNode) ArrayList(java.util.ArrayList)

Example 34 with Template

use of com.ichi2.libanki.template.Template in project Anki-Android by ankidroid.

the class Models method remTemplate.

/**
 * {@inheritDoc}
 */
@Override
public void remTemplate(Model m, JSONObject template) throws ConfirmModSchemaException {
    if (m.getJSONArray("tmpls").length() <= 1) {
        return;
    }
    // find cards using this template
    JSONArray tmpls = m.getJSONArray("tmpls");
    int ord = -1;
    for (int i = 0; i < tmpls.length(); ++i) {
        if (tmpls.getJSONObject(i).equals(template)) {
            ord = i;
            break;
        }
    }
    if (ord == -1) {
        throw new IllegalArgumentException("Invalid template proposed for delete");
    }
    // the code in "isRemTemplateSafe" was in place here in libanki. It is extracted to a method for reuse
    List<Long> cids = getCardIdsForModel(m.getLong("id"), new int[] { ord });
    if (cids == null) {
        Timber.d("remTemplate getCardIdsForModel determined it was unsafe to delete the template");
        return;
    }
    // ok to proceed; remove cards
    Timber.d("remTemplate proceeding to delete the template and %d cards", cids.size());
    mCol.modSchema();
    mCol.remCards(cids);
    // shift ordinals
    mCol.getDb().execute("update cards set ord = ord - 1, usn = ?, mod = ? where nid in (select id from notes where mid = ?) and ord > ?", mCol.usn(), mCol.getTime().intTime(), m.getLong("id"), ord);
    tmpls = m.getJSONArray("tmpls");
    JSONArray tmpls2 = new JSONArray();
    for (int i = 0; i < tmpls.length(); ++i) {
        if (template.equals(tmpls.getJSONObject(i))) {
            continue;
        }
        tmpls2.put(tmpls.getJSONObject(i));
    }
    m.put("tmpls", tmpls2);
    _updateTemplOrds(m);
    save(m);
    Timber.d("remTemplate done working");
}
Also used : JSONArray(com.ichi2.utils.JSONArray)

Example 35 with Template

use of com.ichi2.libanki.template.Template in project Anki-Android by ankidroid.

the class Models method _reqForTemplate.

// 'String f' is unused upstream as well
@SuppressWarnings("PMD.UnusedLocalVariable")
private Object[] _reqForTemplate(Model m, List<String> flds, JSONObject t) {
    int nbFields = flds.size();
    String[] a = new String[nbFields];
    String[] b = new String[nbFields];
    Arrays.fill(a, "ankiflag");
    Arrays.fill(b, "");
    int ord = t.getInt("ord");
    String full = mCol._renderQA(1L, m, 1L, ord, "", a, 0).get("q");
    String empty = mCol._renderQA(1L, m, 1L, ord, "", b, 0).get("q");
    // if full and empty are the same, the template is invalid and there is no way to satisfy it
    if (full.equals(empty)) {
        return new Object[] { REQ_NONE, new JSONArray(), new JSONArray() };
    }
    String type = REQ_ALL;
    JSONArray req = new JSONArray();
    for (int i = 0; i < flds.size(); i++) {
        a[i] = "";
        // if no field content appeared, field is required
        if (!mCol._renderQA(1L, m, 1L, ord, "", a, 0).get("q").contains("ankiflag")) {
            req.put(i);
        }
        a[i] = "ankiflag";
    }
    if (req.length() > 0) {
        return new Object[] { type, req };
    }
    // if there are no required fields, switch to any mode
    type = REQ_ANY;
    req = new JSONArray();
    for (int i = 0; i < flds.size(); i++) {
        b[i] = "1";
        // if not the same as empty, this field can make the card non-blank
        if (!mCol._renderQA(1L, m, 1L, ord, "", b, 0).get("q").equals(empty)) {
            req.put(i);
        }
        b[i] = "";
    }
    return new Object[] { type, req };
}
Also used : JSONArray(com.ichi2.utils.JSONArray) JSONObject(com.ichi2.utils.JSONObject)

Aggregations

JSONObject (com.ichi2.utils.JSONObject)53 Test (org.junit.Test)35 JSONArray (com.ichi2.utils.JSONArray)28 Model (com.ichi2.libanki.Model)24 ArrayList (java.util.ArrayList)16 Intent (android.content.Intent)14 RobolectricTest (com.ichi2.anki.RobolectricTest)14 Collection (com.ichi2.libanki.Collection)12 Note (com.ichi2.libanki.Note)12 SuppressLint (android.annotation.SuppressLint)9 HashMap (java.util.HashMap)8 Bundle (android.os.Bundle)7 JSONException (com.ichi2.utils.JSONException)7 ConfirmModSchemaException (com.ichi2.anki.exception.ConfirmModSchemaException)6 Card (com.ichi2.libanki.Card)6 ShadowActivity (org.robolectric.shadows.ShadowActivity)6 ShadowIntent (org.robolectric.shadows.ShadowIntent)6 View (android.view.View)5 ContentResolver (android.content.ContentResolver)4 ContentValues (android.content.ContentValues)4