def chandler(default, toconf): print( "You can configure comments now. Type '?' (a question mark, sans quotes) to list available comment systems. If you do not want any comments, just leave the field blank." ) answer = ask("Comment system", "") while answer.strip() == "?": print("\n# Available comment systems:") print(SAMPLE_CONF["_SUPPORTED_COMMENT_SYSTEMS"]) print("") answer = ask("Comment system", "") while answer and answer not in LEGAL_VALUES["COMMENT_SYSTEM"]: if answer != "?": print(" ERROR: Nikola does not know this comment system.") print("\n# Available comment systems:") print(SAMPLE_CONF["_SUPPORTED_COMMENT_SYSTEMS"]) print("") answer = ask("Comment system", "") SAMPLE_CONF["COMMENT_SYSTEM"] = answer SAMPLE_CONF["COMMENT_SYSTEM_ID"] = "" if answer: print( "You need to provide the site identifier for your comment system. Consult the Nikola manual for details on what the value should be. (you can leave it empty and come back later)" ) answer = ask("Comment system site identifier", "") SAMPLE_CONF["COMMENT_SYSTEM_ID"] = answer
def chandler(default, toconf): print( "You can configure comments now. Type '?' (a question mark, sans quotes) to list available comment systems. If you do not want any comments, just leave the field blank." ) answer = ask('Comment system', '') while answer.strip() == '?': print('\n# Available comment systems:') print(SAMPLE_CONF['_SUPPORTED_COMMENT_SYSTEMS']) print('') answer = ask('Comment system', '') while answer and answer not in LEGAL_VALUES['COMMENT_SYSTEM']: if answer != '?': print( ' ERROR: Nikola does not know this comment system.') print('\n# Available comment systems:') print(SAMPLE_CONF['_SUPPORTED_COMMENT_SYSTEMS']) print('') answer = ask('Comment system', '') SAMPLE_CONF['COMMENT_SYSTEM'] = answer SAMPLE_CONF['COMMENT_SYSTEM_ID'] = '' if answer: print( "You need to provide the site identifier for your comment system. Consult the Nikola manual for details on what the value should be. (you can leave it empty and come back later)" ) answer = ask('Comment system site identifier', '') SAMPLE_CONF['COMMENT_SYSTEM_ID'] = answer
def lhandler(default, toconf, show_header=True): if show_header: print("We will now ask you to provide the list of languages you want to use.") print( "Please list all the desired languages, comma-separated, using ISO 639-1 codes. The first language will be used as the default." ) print("Type '?' (a question mark, sans quotes) to list available languages.") answer = ask("Language(s) to use", "en") while answer.strip() == "?": print("\n# Available languages:") try: print(SAMPLE_CONF["_SUPPORTED_LANGUAGES"] + "\n") except UnicodeEncodeError: # avoid Unicode characters in supported language names print(unidecode.unidecode(SAMPLE_CONF["_SUPPORTED_LANGUAGES"]) + "\n") answer = ask("Language(s) to use", "en") langs = [i.strip().lower().replace("-", "_") for i in answer.split(",")] for partial, full in LEGAL_VALUES["_TRANSLATIONS_WITH_COUNTRY_SPECIFIERS"].items(): if partial in langs: langs[langs.index(partial)] = full print("NOTICE: Assuming '{0}' instead of '{1}'.".format(full, partial)) default = langs.pop(0) SAMPLE_CONF["DEFAULT_LANG"] = default # format_default_translations_config() is intelligent enough to # return the current value if there are no additional languages. SAMPLE_CONF["TRANSLATIONS"] = format_default_translations_config(langs) # Get messages for navigation_links. In order to do this, we need # to generate a throwaway TRANSLATIONS dict. tr = {default: ""} for l in langs: tr[l] = "./" + l # Assuming that base contains all the locales, and that base does # not inherit from anywhere. try: messages = load_messages(["base"], tr, default) SAMPLE_CONF["NAVIGATION_LINKS"] = format_navigation_links( langs, default, messages, SAMPLE_CONF["STRIP_INDEXES"] ) except nikola.utils.LanguageNotFoundError as e: print(" ERROR: the language '{0}' is not supported.".format(e.lang)) print( " Are you sure you spelled the name correctly? Names are case-sensitive and need to be reproduced as-is (complete with the country specifier, if any)." ) print("\nType '?' (a question mark, sans quotes) to list available languages.") lhandler(default, toconf, show_header=False)
def tzhandler(default, toconf): print("\nPlease choose the correct time zone for your blog. Nikola uses the tz database.") print("You can find your time zone here:") print("https://en.wikipedia.org/wiki/List_of_tz_database_time_zones") print("") answered = False while not answered: try: lz = get_localzone() except: lz = None answer = ask("Time zone", lz if lz else "UTC") tz = dateutil.tz.gettz(answer) if tz is None: print(" WARNING: Time zone not found. Searching list of time zones for a match.") zonesfile = tarfile.open(fileobj=dateutil.zoneinfo.getzoneinfofile_stream()) zonenames = [zone for zone in zonesfile.getnames() if answer.lower() in zone.lower()] if len(zonenames) == 1: tz = dateutil.tz.gettz(zonenames[0]) answer = zonenames[0] print(" Picking '{0}'.".format(answer)) elif len(zonenames) > 1: print(" The following time zones match your query:") print(" " + "\n ".join(zonenames)) continue if tz is not None: time = datetime.datetime.now(tz).strftime("%H:%M:%S") print(" Current time in {0}: {1}".format(answer, time)) answered = ask_yesno("Use this time zone?", True) else: print(" ERROR: No matches found. Please try again.") SAMPLE_CONF["TIMEZONE"] = answer
def urlhandler(default, toconf): answer = ask("Site URL", "https://example.com/") try: answer = answer.decode("utf-8") except (AttributeError, UnicodeDecodeError): pass if not answer.startswith("http"): print(" ERROR: You must specify a protocol (http or https).") urlhandler(default, toconf) return if not answer.endswith("/"): print(" The URL does not end in '/' -- adding it.") answer += "/" dst_url = urlsplit(answer) try: dst_url.netloc.encode("ascii") except (UnicodeEncodeError, UnicodeDecodeError): # The IDN contains characters beyond ASCII. We must convert it # to Punycode. (Issue #1644) nl = dst_url.netloc.encode("idna") answer = urlunsplit((dst_url.scheme, nl, dst_url.path, dst_url.query, dst_url.fragment)) print(" Converting to Punycode:", answer) SAMPLE_CONF["SITE_URL"] = answer
def urlhandler(default, toconf): answer = ask('Site URL', 'https://example.com/') try: answer = answer.decode('utf-8') except (AttributeError, UnicodeDecodeError): pass if not answer.startswith(u'http'): print( " ERROR: You must specify a protocol (http or https).") urlhandler(default, toconf) return if not answer.endswith('/'): print(" The URL does not end in '/' -- adding it.") answer += '/' dst_url = urlsplit(answer) try: dst_url.netloc.encode('ascii') except (UnicodeEncodeError, UnicodeDecodeError): # The IDN contains characters beyond ASCII. We must convert it # to Punycode. (Issue #1644) nl = dst_url.netloc.encode('idna') answer = urlunsplit((dst_url.scheme, nl, dst_url.path, dst_url.query, dst_url.fragment)) print(" Converting to Punycode:", answer) SAMPLE_CONF['SITE_URL'] = answer
def tzhandler(default, toconf): print("\nPlease choose the correct time zone for your blog. Nikola uses the tz database.") print("You can find your time zone here:") print("https://en.wikipedia.org/wiki/List_of_tz_database_time_zones") print("") answered = False while not answered: try: lz = get_localzone() except: lz = None answer = ask('Time zone', lz if lz else "UTC") tz = dateutil.tz.gettz(answer) if tz is None: print(" WARNING: Time zone not found. Searching list of time zones for a match.") zonesfile = tarfile.open(fileobj=dateutil.zoneinfo.getzoneinfofile_stream()) zonenames = [zone for zone in zonesfile.getnames() if answer.lower() in zone.lower()] if len(zonenames) == 1: tz = dateutil.tz.gettz(zonenames[0]) answer = zonenames[0] print(" Picking '{0}'.".format(answer)) elif len(zonenames) > 1: print(" The following time zones match your query:") print(' ' + '\n '.join(zonenames)) continue if tz is not None: time = datetime.datetime.now(tz).strftime('%H:%M:%S') print(" Current time in {0}: {1}".format(answer, time)) answered = ask_yesno("Use this time zone?", True) else: print(" ERROR: No matches found. Please try again.") SAMPLE_CONF['TIMEZONE'] = answer
def tzhandler(default, toconf): print("\nPlease choose the correct time zone for your blog. Nikola uses the tz database.") print("You can find your time zone here:") print("http://en.wikipedia.org/wiki/List_of_tz_database_time_zones") print("") answered = False while not answered: try: lz = get_localzone() except: lz = None answer = ask('Time zone', lz if lz else "UTC") tz = dateutil.tz.gettz(answer) if tz is None: print(" WARNING: Time zone not found. Searching most common timezones for a match.") zonesfile = tarfile.TarFile.open(os.path.join(dateutil.zoneinfo.ZONEINFOFILE)) zonenames = [zone for zone in zonesfile.getnames() if answer.lower() in zone.lower()] if len(zonenames) == 1: tz = dateutil.tz.gettz(zonenames[0]) elif len(zonenames) > 1: print(" Could not pick one timezone. Choose one of the following:") print(' ' + '\n '.join(zonenames)) continue if tz is not None: time = datetime.datetime.now(tz).strftime('%H:%M:%S') print(" Current time in {0}: {1}".format(answer, time)) answered = ask_yesno("Use this time zone?", True) else: print(" ERROR: Time zone not found. Please try again. Time zones are case-sensitive.") SAMPLE_CONF['TIMEZONE'] = answer
def tzhandler(default, toconf): print( "\nPlease choose the correct time zone for your blog. Nikola uses the tz database." ) print("You can find your time zone here:") print( "http://en.wikipedia.org/wiki/List_of_tz_database_time_zones") print("") answered = False while not answered: try: lz = get_localzone() except: lz = None answer = ask('Time zone', lz if lz else "UTC") tz = dateutil.tz.gettz(answer) if tz is not None: time = datetime.datetime.now(tz).strftime('%H:%M:%S') print(" Current time in {0}: {1}".format(answer, time)) answered = ask_yesno("Use this time zone?", True) else: print( " ERROR: Time zone not found. Please try again. Time zones are case-sensitive." ) SAMPLE_CONF['TIMEZONE'] = answer
def lhandler(default, toconf, show_header=True): if show_header: print("We will now ask you to provide the list of languages you want to use.") print("Please list all the desired languages, comma-separated, using ISO 639-1 codes. The first language will be used as the default.") print("Type '?' (a question mark, sans quotes) to list available languages.") answer = ask('Language(s) to use', 'en') while answer.strip() == '?': print('\n# Available languages:') try: print(SAMPLE_CONF['_SUPPORTED_LANGUAGES'] + '\n') except UnicodeEncodeError: # avoid Unicode characters in supported language names print(unidecode.unidecode(SAMPLE_CONF['_SUPPORTED_LANGUAGES']) + '\n') answer = ask('Language(s) to use', 'en') langs = [i.strip().lower().replace('-', '_') for i in answer.split(',')] for partial, full in LEGAL_VALUES['_TRANSLATIONS_WITH_COUNTRY_SPECIFIERS'].items(): if partial in langs: langs[langs.index(partial)] = full print("NOTICE: Assuming '{0}' instead of '{1}'.".format(full, partial)) default = langs.pop(0) SAMPLE_CONF['DEFAULT_LANG'] = default # format_default_translations_config() is intelligent enough to # return the current value if there are no additional languages. SAMPLE_CONF['TRANSLATIONS'] = format_default_translations_config(langs) # Get messages for navigation_links. In order to do this, we need # to generate a throwaway TRANSLATIONS dict. tr = {default: ''} for l in langs: tr[l] = './' + l # Assuming that base contains all the locales, and that base does # not inherit from anywhere. try: messages = load_messages(['base'], tr, default) SAMPLE_CONF['NAVIGATION_LINKS'] = format_navigation_links(langs, default, messages, SAMPLE_CONF['STRIP_INDEXES']) except nikola.utils.LanguageNotFoundError as e: print(" ERROR: the language '{0}' is not supported.".format(e.lang)) print(" Are you sure you spelled the name correctly? Names are case-sensitive and need to be reproduced as-is (complete with the country specifier, if any).") print("\nType '?' (a question mark, sans quotes) to list available languages.") lhandler(default, toconf, show_header=False)
def tzhandler(default, toconf): print( "\nPlease choose the correct time zone for your blog. Nikola uses the tz database." ) print("You can find your time zone here:") print( "http://en.wikipedia.org/wiki/List_of_tz_database_time_zones") print("") answered = False while not answered: try: lz = get_localzone() except: lz = None answer = ask('Time zone', lz if lz else "UTC") tz = dateutil.tz.gettz(answer) if tz is None: print( " WARNING: Time zone not found. Searching most common timezones for a match." ) zonesfile = tarfile.TarFile.open( os.path.join(dateutil.zoneinfo.ZONEINFOFILE)) zonenames = [ zone for zone in zonesfile.getnames() if answer.lower() in zone.lower() ] if len(zonenames) == 1: tz = dateutil.tz.gettz(zonenames[0]) elif len(zonenames) > 1: print( " Could not pick one timezone. Choose one of the following:" ) print(' ' + '\n '.join(zonenames)) continue if tz is not None: time = datetime.datetime.now(tz).strftime('%H:%M:%S') print(" Current time in {0}: {1}".format(answer, time)) answered = ask_yesno("Use this time zone?", True) else: print( " ERROR: Time zone not found. Please try again. Time zones are case-sensitive." ) SAMPLE_CONF['TIMEZONE'] = answer
def tzhandler(default, toconf): print("\nPlease choose the correct time zone for your blog. Nikola uses the tz database.") print("You can find your time zone here:") print("http://en.wikipedia.org/wiki/List_of_tz_database_time_zones") print("") answered = False while not answered: answer = ask("Time zone", "UTC") tz = dateutil.tz.gettz(answer) if tz is not None: time = datetime.datetime.now(tz).strftime("%H:%M:%S") print(" Current time in {0}: {1}".format(answer, time)) answered = ask_yesno("Use this time zone?", True) else: print(" ERROR: Time zone not found. Please try again. Time zones are case-sensitive.") SAMPLE_CONF["TIMEZONE"] = answer
def ask_questions(target): """Ask some questions about Nikola.""" def lhandler(default, toconf, show_header=True): if show_header: print("We will now ask you to provide the list of languages you want to use.") print( "Please list all the desired languages, comma-separated, using ISO 639-1 codes. The first language will be used as the default." ) print("Type '?' (a question mark, sans quotes) to list available languages.") answer = ask("Language(s) to use", "en") while answer.strip() == "?": print("\n# Available languages:") print(SAMPLE_CONF["_SUPPORTED_LANGUAGES"] + "\n") answer = ask("Language(s) to use", "en") langs = [i.strip().lower().replace("-", "_") for i in answer.split(",")] for partial, full in LEGAL_VALUES["_TRANSLATIONS_WITH_COUNTRY_SPECIFIERS"].items(): if partial in langs: langs[langs.index(partial)] = full print("NOTICE: Assuming '{0}' instead of '{1}'.".format(full, partial)) default = langs.pop(0) SAMPLE_CONF["DEFAULT_LANG"] = default # format_default_translations_config() is intelligent enough to # return the current value if there are no additional languages. SAMPLE_CONF["TRANSLATIONS"] = format_default_translations_config(langs) # Get messages for navigation_links. In order to do this, we need # to generate a throwaway TRANSLATIONS dict. tr = {default: ""} for l in langs: tr[l] = "./" + l # Assuming that base contains all the locales, and that base does # not inherit from anywhere. try: messages = load_messages(["base"], tr, default) SAMPLE_CONF["NAVIGATION_LINKS"] = format_navigation_links(langs, default, messages) except nikola.utils.LanguageNotFoundError as e: print(" ERROR: the language '{0}' is not supported.".format(e.lang)) print( " Are you sure you spelled the name correctly? Names are case-sensitive and need to be reproduced as-is (complete with the country specifier, if any)." ) print("\nType '?' (a question mark, sans quotes) to list available languages.") lhandler(default, toconf, show_header=False) def tzhandler(default, toconf): print("\nPlease choose the correct time zone for your blog. Nikola uses the tz database.") print("You can find your time zone here:") print("http://en.wikipedia.org/wiki/List_of_tz_database_time_zones") print("") answered = False while not answered: answer = ask("Time zone", "UTC") tz = dateutil.tz.gettz(answer) if tz is not None: time = datetime.datetime.now(tz).strftime("%H:%M:%S") print(" Current time in {0}: {1}".format(answer, time)) answered = ask_yesno("Use this time zone?", True) else: print(" ERROR: Time zone not found. Please try again. Time zones are case-sensitive.") SAMPLE_CONF["TIMEZONE"] = answer def chandler(default, toconf): print( "You can configure comments now. Type '?' (a question mark, sans quotes) to list available comment systems. If you do not want any comments, just leave the field blank." ) answer = ask("Comment system", "") while answer.strip() == "?": print("\n# Available comment systems:") print(SAMPLE_CONF["_SUPPORTED_COMMENT_SYSTEMS"]) print("") answer = ask("Comment system", "") while answer and answer not in LEGAL_VALUES["COMMENT_SYSTEM"]: if answer != "?": print(" ERROR: Nikola does not know this comment system.") print("\n# Available comment systems:") print(SAMPLE_CONF["_SUPPORTED_COMMENT_SYSTEMS"]) print("") answer = ask("Comment system", "") SAMPLE_CONF["COMMENT_SYSTEM"] = answer SAMPLE_CONF["COMMENT_SYSTEM_ID"] = "" if answer: print( "You need to provide the site identifier for your comment system. Consult the Nikola manual for details on what the value should be. (you can leave it empty and come back later)" ) answer = ask("Comment system site identifier", "") SAMPLE_CONF["COMMENT_SYSTEM_ID"] = answer STORAGE = {"target": target} questions = [ ("Questions about the site", None, None, None), # query, default, toconf, destination ("Destination", None, False, "!target"), ("Site title", "My Nikola Site", True, "BLOG_TITLE"), ("Site author", "Nikola Tesla", True, "BLOG_AUTHOR"), ("Site author’s e-mail", "*****@*****.**", True, "BLOG_EMAIL"), ("Site description", "This is a demo site for Nikola.", True, "BLOG_DESCRIPTION"), ("Site URL", "http://getnikola.com/", True, "SITE_URL"), ("Questions about languages and locales", None, None, None), (lhandler, None, True, True), (tzhandler, None, True, True), ("Questions about comments", None, None, None), (chandler, None, True, True), ] print("Creating Nikola Site") print("====================\n") print( "This is Nikola v{0}. We will now ask you a few easy questions about your new site.".format( nikola.__version__ ) ) print( "If you do not want to answer and want to go with the defaults instead, simply restart with the `-q` parameter." ) for query, default, toconf, destination in questions: if target and destination == "!target": # Skip the destination question if we know it already pass else: if default is toconf is destination is None: print("--- {0} ---".format(query)) elif destination is True: query(default, toconf) else: answer = ask(query, default) if toconf: SAMPLE_CONF[destination] = answer if destination == "!target": while not answer: print(" ERROR: you need to specify a target directory.\n") answer = ask(query, default) STORAGE["target"] = answer print("\nThat's it, Nikola is now configured. Make sure to edit conf.py to your liking.") print( "If you are looking for themes and addons, check out http://themes.getnikola.com/ and http://plugins.getnikola.com/." ) print("Have fun!") return STORAGE
def _execute(self, options, args): """Create a new post or page.""" global LOGGER compiler_names = [ p.name for p in self.site.plugin_manager.getPluginsOfCategory( "PageCompiler") ] if len(args) > 1: print(self.help()) return False elif args: path = args[0] else: path = None # Even though stuff was split into `new_page`, it’s easier to do it # here not to duplicate the code. is_page = options.get('is_page', False) is_post = not is_page content_type = 'page' if is_page else 'post' title = options['title'] or None author = options['author'] or '' tags = options['tags'] onefile = options['onefile'] twofile = options['twofile'] import_file = options['import'] wants_available = options['available-formats'] date_path_opt = options['date-path'] date_path_auto = self.site.config[ 'NEW_POST_DATE_PATH'] and content_type == 'post' date_path_format = self.site.config['NEW_POST_DATE_PATH_FORMAT'].strip( '/') if wants_available: self.print_compilers() return if is_page: LOGGER = PAGELOGGER else: LOGGER = POSTLOGGER if twofile: onefile = False if not onefile and not twofile: onefile = self.site.config.get('ONE_FILE_POSTS', True) content_format = options['content_format'] content_subformat = None if "@" in content_format: content_format, content_subformat = content_format.split("@") if not content_format and path and not os.path.isdir(path): # content_format not specified. If path was given, use # it to guess (Issue #2798) extension = os.path.splitext(path)[-1] for compiler, extensions in self.site.config['COMPILERS'].items(): if extension in extensions: content_format = compiler if not content_format: LOGGER.error( "Unknown {0} extension {1}, maybe you need to install a plugin or enable an existing one?" .format(content_type, extension)) return elif not content_format and import_file: # content_format not specified. If import_file was given, use # it to guess (Issue #2798) extension = os.path.splitext(import_file)[-1] for compiler, extensions in self.site.config['COMPILERS'].items(): if extension in extensions: content_format = compiler if not content_format: LOGGER.error( "Unknown {0} extension {1}, maybe you need to install a plugin or enable an existing one?" .format(content_type, extension)) return elif not content_format: # Issue #400 content_format = get_default_compiler( is_post, self.site.config['COMPILERS'], self.site.config['post_pages']) elif content_format not in compiler_names: LOGGER.error( "Unknown {0} format {1}, maybe you need to install a plugin or enable an existing one?" .format(content_type, content_format)) self.print_compilers() return compiler_plugin = self.site.plugin_manager.getPluginByName( content_format, "PageCompiler").plugin_object # Guess where we should put this entry = self.filter_post_pages(content_format, is_post) if entry is False: return 1 if import_file: print("Importing Existing {xx}".format(xx=content_type.title())) print("-----------------------\n") else: print("Creating New {xx}".format(xx=content_type.title())) print("-----------------\n") if title is not None: print("Title:", title) else: while not title: title = utils.ask('Title') if isinstance(title, utils.bytes_str): try: title = title.decode(sys.stdin.encoding) except (AttributeError, TypeError): # for tests title = title.decode('utf-8') title = title.strip() if not path: slug = utils.slugify(title, lang=self.site.default_lang) else: if isinstance(path, utils.bytes_str): try: path = path.decode(sys.stdin.encoding) except (AttributeError, TypeError): # for tests path = path.decode('utf-8') if os.path.isdir(path): # If the user provides a directory, add the file name generated from title (Issue #2651) slug = utils.slugify(title, lang=self.site.default_lang) pattern = os.path.basename(entry[0]) suffix = pattern[1:] path = os.path.join(path, slug + suffix) else: slug = utils.slugify(os.path.splitext( os.path.basename(path))[0], lang=self.site.default_lang) if isinstance(author, utils.bytes_str): try: author = author.decode(sys.stdin.encoding) except (AttributeError, TypeError): # for tests author = author.decode('utf-8') # Calculate the date to use for the content # SCHEDULE_ALL is post-only (Issue #2921) schedule = options['schedule'] or (self.site.config['SCHEDULE_ALL'] and is_post) rule = self.site.config['SCHEDULE_RULE'] self.site.scan_posts() timeline = self.site.timeline last_date = None if not timeline else timeline[0].date date, dateobj = get_date(schedule, rule, last_date, self.site.tzinfo, self.site.config['FORCE_ISO8601']) data = { 'title': title, 'slug': slug, 'date': date, 'tags': tags, 'link': '', 'description': '', 'type': 'text', } if not path: pattern = os.path.basename(entry[0]) suffix = pattern[1:] output_path = os.path.dirname(entry[0]) if date_path_auto or date_path_opt: output_path += os.sep + dateobj.strftime(date_path_format) txt_path = os.path.join(output_path, slug + suffix) meta_path = os.path.join(output_path, slug + ".meta") else: if date_path_opt: LOGGER.warning("A path has been specified, ignoring -d") txt_path = os.path.join(self.site.original_cwd, path) meta_path = os.path.splitext(txt_path)[0] + ".meta" if (not onefile and os.path.isfile(meta_path)) or \ os.path.isfile(txt_path): # Emit an event when a post exists event = dict(path=txt_path) if not onefile: # write metadata file event['meta_path'] = meta_path signal('existing_' + content_type).send(self, **event) LOGGER.error("The title already exists!") LOGGER.info("Existing {0}'s text is at: {1}".format( content_type, txt_path)) if not onefile: LOGGER.info("Existing {0}'s metadata is at: {1}".format( content_type, meta_path)) return 8 d_name = os.path.dirname(txt_path) utils.makedirs(d_name) metadata = {} if author: metadata['author'] = author metadata.update(self.site.config['ADDITIONAL_METADATA']) data.update(metadata) # ipynb plugin needs the Jupyter kernel info. We get the kernel name # from the content_subformat and pass it to the compiler in the metadata if content_format == "ipynb" and content_subformat is not None: metadata["jupyter_kernel"] = content_subformat # Override onefile if not really supported. if not compiler_plugin.supports_onefile and onefile: onefile = False LOGGER.warning('This compiler does not support one-file posts.') if onefile and import_file: with io.open(import_file, 'r', encoding='utf-8') as fh: content = fh.read() elif not import_file: if is_page: content = self.site.MESSAGES[ self.site.default_lang]["Write your page here."] else: content = self.site.MESSAGES[ self.site.default_lang]["Write your post here."] if (not onefile) and import_file: # Two-file posts are copied on import (Issue #2380) shutil.copy(import_file, txt_path) else: compiler_plugin.create_post(txt_path, content=content, onefile=onefile, title=title, slug=slug, date=date, tags=tags, is_page=is_page, **metadata) event = dict(path=txt_path) if not onefile: # write metadata file with io.open(meta_path, "w+", encoding="utf8") as fd: fd.write( utils.write_metadata(data, comment_wrap=False, site=self.site)) LOGGER.info("Your {0}'s metadata is at: {1}".format( content_type, meta_path)) event['meta_path'] = meta_path LOGGER.info("Your {0}'s text is at: {1}".format( content_type, txt_path)) signal('new_' + content_type).send(self, **event) if options['edit']: editor = os.getenv('EDITOR', '').split() to_run = editor + [txt_path] if not onefile: to_run.append(meta_path) if editor: subprocess.call(to_run) else: LOGGER.error( 'The $EDITOR environment variable is not set, cannot edit the post with \'-e\'. Please edit the post manually.' )
def _execute(self, options, args): """Create a new post or page.""" global LOGGER compiler_names = [p.name for p in self.site.plugin_manager.getPluginsOfCategory( "PageCompiler")] if len(args) > 1: print(self.help()) return False elif args: path = args[0] else: path = None # Even though stuff was split into `new_page`, it’s easier to do it # here not to duplicate the code. is_page = options.get('is_page', False) is_post = not is_page content_type = 'page' if is_page else 'post' title = options['title'] or None author = options['author'] or '' tags = options['tags'] onefile = options['onefile'] twofile = options['twofile'] import_file = options['import'] wants_available = options['available-formats'] date_path_opt = options['date-path'] date_path_auto = self.site.config['NEW_POST_DATE_PATH'] date_path_format = self.site.config['NEW_POST_DATE_PATH_FORMAT'].strip('/') if wants_available: self.print_compilers() return if is_page: LOGGER = PAGELOGGER else: LOGGER = POSTLOGGER if twofile: onefile = False if not onefile and not twofile: onefile = self.site.config.get('ONE_FILE_POSTS', True) content_format = options['content_format'] content_subformat = None if "@" in content_format: content_format, content_subformat = content_format.split("@") if not content_format: # Issue #400 content_format = get_default_compiler( is_post, self.site.config['COMPILERS'], self.site.config['post_pages']) if content_format not in compiler_names: LOGGER.error("Unknown {0} format {1}, maybe you need to install a plugin or enable an existing one?".format(content_type, content_format)) self.print_compilers() return compiler_plugin = self.site.plugin_manager.getPluginByName( content_format, "PageCompiler").plugin_object # Guess where we should put this entry = self.filter_post_pages(content_format, is_post) if entry is False: return 1 if import_file: print("Importing Existing {xx}".format(xx=content_type.title())) print("-----------------------\n") else: print("Creating New {xx}".format(xx=content_type.title())) print("-----------------\n") if title is not None: print("Title:", title) else: while not title: title = utils.ask('Title') if isinstance(title, utils.bytes_str): try: title = title.decode(sys.stdin.encoding) except (AttributeError, TypeError): # for tests title = title.decode('utf-8') title = title.strip() if not path: slug = utils.slugify(title, lang=self.site.default_lang) else: if isinstance(path, utils.bytes_str): try: path = path.decode(sys.stdin.encoding) except (AttributeError, TypeError): # for tests path = path.decode('utf-8') slug = utils.slugify(os.path.splitext(os.path.basename(path))[0], lang=self.site.default_lang) if isinstance(author, utils.bytes_str): try: author = author.decode(sys.stdin.encoding) except (AttributeError, TypeError): # for tests author = author.decode('utf-8') # Calculate the date to use for the content schedule = options['schedule'] or self.site.config['SCHEDULE_ALL'] rule = self.site.config['SCHEDULE_RULE'] self.site.scan_posts() timeline = self.site.timeline last_date = None if not timeline else timeline[0].date date, dateobj = get_date(schedule, rule, last_date, self.site.tzinfo, self.site.config['FORCE_ISO8601']) data = { 'title': title, 'slug': slug, 'date': date, 'tags': tags, 'link': '', 'description': '', 'type': 'text', } if not path: pattern = os.path.basename(entry[0]) suffix = pattern[1:] output_path = os.path.dirname(entry[0]) if date_path_auto or date_path_opt: output_path += os.sep + dateobj.strftime(date_path_format) txt_path = os.path.join(output_path, slug + suffix) meta_path = os.path.join(output_path, slug + ".meta") else: if date_path_opt: LOGGER.warn("A path has been specified, ignoring -d") txt_path = os.path.join(self.site.original_cwd, path) meta_path = os.path.splitext(txt_path)[0] + ".meta" if (not onefile and os.path.isfile(meta_path)) or \ os.path.isfile(txt_path): # Emit an event when a post exists event = dict(path=txt_path) if not onefile: # write metadata file event['meta_path'] = meta_path signal('existing_' + content_type).send(self, **event) LOGGER.error("The title already exists!") LOGGER.info("Existing {0}'s text is at: {1}".format(content_type, txt_path)) if not onefile: LOGGER.info("Existing {0}'s metadata is at: {1}".format(content_type, meta_path)) return 8 d_name = os.path.dirname(txt_path) utils.makedirs(d_name) metadata = {} if author: metadata['author'] = author metadata.update(self.site.config['ADDITIONAL_METADATA']) data.update(metadata) # ipynb plugin needs the ipython kernel info. We get the kernel name # from the content_subformat and pass it to the compiler in the metadata if content_format == "ipynb" and content_subformat is not None: metadata["ipython_kernel"] = content_subformat # Override onefile if not really supported. if not compiler_plugin.supports_onefile and onefile: onefile = False LOGGER.warn('This compiler does not support one-file posts.') if onefile and import_file: with io.open(import_file, 'r', encoding='utf-8') as fh: content = fh.read() elif not import_file: if is_page: content = self.site.MESSAGES[self.site.default_lang]["Write your page here."] else: content = self.site.MESSAGES[self.site.default_lang]["Write your post here."] if (not onefile) and import_file: # Two-file posts are copied on import (Issue #2380) shutil.copy(import_file, txt_path) else: compiler_plugin.create_post( txt_path, content=content, onefile=onefile, title=title, slug=slug, date=date, tags=tags, is_page=is_page, **metadata) event = dict(path=txt_path) if not onefile: # write metadata file with io.open(meta_path, "w+", encoding="utf8") as fd: fd.write(utils.write_metadata(data)) LOGGER.info("Your {0}'s metadata is at: {1}".format(content_type, meta_path)) event['meta_path'] = meta_path LOGGER.info("Your {0}'s text is at: {1}".format(content_type, txt_path)) signal('new_' + content_type).send(self, **event) if options['edit']: editor = os.getenv('EDITOR', '').split() to_run = editor + [txt_path] if not onefile: to_run.append(meta_path) if editor: subprocess.call(to_run) else: LOGGER.error('$EDITOR not set, cannot edit the post. Please do it manually.')
def _execute(self, options, args): """Create a new post or page.""" global LOGGER compiler_names = [p.name for p in self.site.plugin_manager.getPluginsOfCategory("PageCompiler")] if len(args) > 1: print(self.help()) return False elif args: path = args[0] else: path = None # Even though stuff was split into `new_page`, it’s easier to do it # here not to duplicate the code. is_page = options.get("is_page", False) is_post = not is_page content_type = "page" if is_page else "post" title = options["title"] or None author = options["author"] or "" tags = options["tags"] onefile = options["onefile"] twofile = options["twofile"] import_file = options["import"] wants_available = options["available-formats"] if wants_available: self.print_compilers() return if is_page: LOGGER = PAGELOGGER else: LOGGER = POSTLOGGER if twofile: onefile = False if not onefile and not twofile: onefile = self.site.config.get("ONE_FILE_POSTS", True) content_format = options["content_format"] content_subformat = None if "@" in content_format: content_format, content_subformat = content_format.split("@") if not content_format: # Issue #400 content_format = get_default_compiler( is_post, self.site.config["COMPILERS"], self.site.config["post_pages"] ) if content_format not in compiler_names: LOGGER.error( "Unknown {0} format {1}, maybe you need to install a plugin?".format(content_type, content_format) ) self.print_compilers() return compiler_plugin = self.site.plugin_manager.getPluginByName(content_format, "PageCompiler").plugin_object # Guess where we should put this entry = self.filter_post_pages(content_format, is_post) if entry is False: return 1 if import_file: print("Importing Existing {xx}".format(xx=content_type.title())) print("-----------------------\n") else: print("Creating New {xx}".format(xx=content_type.title())) print("-----------------\n") if title is not None: print("Title:", title) else: while not title: title = utils.ask("Title") if isinstance(title, utils.bytes_str): try: title = title.decode(sys.stdin.encoding) except (AttributeError, TypeError): # for tests title = title.decode("utf-8") title = title.strip() if not path: slug = utils.slugify(title) else: if isinstance(path, utils.bytes_str): try: path = path.decode(sys.stdin.encoding) except (AttributeError, TypeError): # for tests path = path.decode("utf-8") slug = utils.slugify(os.path.splitext(os.path.basename(path))[0]) if isinstance(author, utils.bytes_str): try: author = author.decode(sys.stdin.encoding) except (AttributeError, TypeError): # for tests author = author.decode("utf-8") # Calculate the date to use for the content schedule = options["schedule"] or self.site.config["SCHEDULE_ALL"] rule = self.site.config["SCHEDULE_RULE"] self.site.scan_posts() timeline = self.site.timeline last_date = None if not timeline else timeline[0].date date = get_date(schedule, rule, last_date, self.site.tzinfo, self.site.config["FORCE_ISO8601"]) data = {"title": title, "slug": slug, "date": date, "tags": tags, "link": "", "description": "", "type": "text"} output_path = os.path.dirname(entry[0]) meta_path = os.path.join(output_path, slug + ".meta") pattern = os.path.basename(entry[0]) suffix = pattern[1:] if not path: txt_path = os.path.join(output_path, slug + suffix) else: txt_path = os.path.join(self.site.original_cwd, path) if (not onefile and os.path.isfile(meta_path)) or os.path.isfile(txt_path): # Emit an event when a post exists event = dict(path=txt_path) if not onefile: # write metadata file event["meta_path"] = meta_path signal("existing_" + content_type).send(self, **event) LOGGER.error("The title already exists!") return 8 d_name = os.path.dirname(txt_path) utils.makedirs(d_name) metadata = {} if author: metadata["author"] = author metadata.update(self.site.config["ADDITIONAL_METADATA"]) data.update(metadata) # ipynb plugin needs the ipython kernel info. We get the kernel name # from the content_subformat and pass it to the compiler in the metadata if content_format == "ipynb" and content_subformat is not None: metadata["ipython_kernel"] = content_subformat # Override onefile if not really supported. if not compiler_plugin.supports_onefile and onefile: onefile = False LOGGER.warn("This compiler does not support one-file posts.") if import_file: with io.open(import_file, "r", encoding="utf-8") as fh: content = fh.read() else: if is_page: content = self.site.MESSAGES[self.site.default_lang]["Write your page here."] else: content = self.site.MESSAGES[self.site.default_lang]["Write your post here."] compiler_plugin.create_post( txt_path, content=content, onefile=onefile, title=title, slug=slug, date=date, tags=tags, is_page=is_page, **metadata ) event = dict(path=txt_path) if not onefile: # write metadata file with io.open(meta_path, "w+", encoding="utf8") as fd: fd.write(utils.write_metadata(data)) LOGGER.info("Your {0}'s metadata is at: {1}".format(content_type, meta_path)) event["meta_path"] = meta_path LOGGER.info("Your {0}'s text is at: {1}".format(content_type, txt_path)) signal("new_" + content_type).send(self, **event) if options["edit"]: editor = os.getenv("EDITOR", "").split() to_run = editor + [txt_path] if not onefile: to_run.append(meta_path) if editor: subprocess.call(to_run) else: LOGGER.error("$EDITOR not set, cannot edit the post. Please do it manually.")
def _execute(self, options, args): """Create a new post or page.""" global LOGGER compiler_names = [p.name for p in self.site.plugin_manager.getPluginsOfCategory( "PageCompiler")] if len(args) > 1: print(self.help()) return False elif args: path = args[0] else: path = None # Even though stuff was split into `new_page`, it’s easier to do it # here not to duplicate the code. is_page = options.get('is_page', False) is_post = not is_page content_type = 'page' if is_page else 'post' title = options['title'] or None author = options['author'] or '' tags = options['tags'] onefile = options['onefile'] twofile = options['twofile'] import_file = options['import'] if is_page: LOGGER = PAGELOGGER else: LOGGER = POSTLOGGER if twofile: onefile = False if not onefile and not twofile: onefile = self.site.config.get('ONE_FILE_POSTS', True) content_format = options['content_format'] if not content_format: # Issue #400 content_format = get_default_compiler( is_post, self.site.config['COMPILERS'], self.site.config['post_pages']) if content_format not in compiler_names: LOGGER.error("Unknown {0} format {1}".format(content_type, content_format)) return compiler_plugin = self.site.plugin_manager.getPluginByName( content_format, "PageCompiler").plugin_object # Guess where we should put this entry = filter_post_pages(content_format, is_post, self.site.config['COMPILERS'], self.site.config['post_pages']) if import_file: print("Importing Existing {xx}".format(xx=content_type.title())) print("-----------------------\n") else: print("Creating New {xx}".format(xx=content_type.title())) print("-----------------\n") if title is not None: print("Title:", title) else: while not title: title = utils.ask('Title') if isinstance(title, utils.bytes_str): try: title = title.decode(sys.stdin.encoding) except (AttributeError, TypeError): # for tests title = title.decode('utf-8') title = title.strip() if not path: slug = utils.slugify(title) else: if isinstance(path, utils.bytes_str): try: path = path.decode(sys.stdin.encoding) except (AttributeError, TypeError): # for tests path = path.decode('utf-8') slug = utils.slugify(os.path.splitext(os.path.basename(path))[0]) if isinstance(author, utils.bytes_str): try: author = author.decode(sys.stdin.encoding) except (AttributeError, TypeError): # for tests author = author.decode('utf-8') # Calculate the date to use for the content schedule = options['schedule'] or self.site.config['SCHEDULE_ALL'] rule = self.site.config['SCHEDULE_RULE'] self.site.scan_posts() timeline = self.site.timeline last_date = None if not timeline else timeline[0].date date = get_date(schedule, rule, last_date, self.site.tzinfo, self.site.config['FORCE_ISO8601']) data = { 'title': title, 'slug': slug, 'date': date, 'tags': tags, 'link': '', 'description': '', 'type': 'text', } output_path = os.path.dirname(entry[0]) meta_path = os.path.join(output_path, slug + ".meta") pattern = os.path.basename(entry[0]) suffix = pattern[1:] if not path: txt_path = os.path.join(output_path, slug + suffix) else: txt_path = path if (not onefile and os.path.isfile(meta_path)) or \ os.path.isfile(txt_path): LOGGER.error("The title already exists!") exit(8) d_name = os.path.dirname(txt_path) utils.makedirs(d_name) metadata = {} if author: metadata['author'] = author metadata.update(self.site.config['ADDITIONAL_METADATA']) data.update(metadata) # Override onefile if not really supported. if not compiler_plugin.supports_onefile and onefile: onefile = False LOGGER.warn('This compiler does not support one-file posts.') if import_file: with io.open(import_file, 'r', encoding='utf-8') as fh: content = fh.read() else: # ipynb's create_post depends on this exact string, take care # if you're changing it content = "Write your {0} here.".format('page' if is_page else 'post') compiler_plugin.create_post( txt_path, content=content, onefile=onefile, title=title, slug=slug, date=date, tags=tags, is_page=is_page, **metadata) event = dict(path=txt_path) if not onefile: # write metadata file with io.open(meta_path, "w+", encoding="utf8") as fd: fd.write(utils.write_metadata(data)) LOGGER.info("Your {0}'s metadata is at: {1}".format(content_type, meta_path)) event['meta_path'] = meta_path LOGGER.info("Your {0}'s text is at: {1}".format(content_type, txt_path)) signal('new_' + content_type).send(self, **event) if options['edit']: editor = os.getenv('EDITOR', '').split() to_run = editor + [txt_path] if not onefile: to_run.append(meta_path) if editor: subprocess.call(to_run) else: LOGGER.error('$EDITOR not set, cannot edit the post. Please do it manually.')
def ask_questions(target): """Ask some questions about Nikola.""" def lhandler(default, toconf, show_header=True): if show_header: print("We will now ask you to provide the list of languages you want to use.") print("Please list all the desired languages, comma-separated, using ISO 639-1 codes. The first language will be used as the default.") print("Type '?' (a question mark, sans quotes) to list available languages.") answer = ask('Language(s) to use', 'en') while answer.strip() == '?': print('\n# Available languages:') try: print(SAMPLE_CONF['_SUPPORTED_LANGUAGES'] + '\n') except UnicodeEncodeError: # avoid Unicode characters in supported language names print(unidecode.unidecode(SAMPLE_CONF['_SUPPORTED_LANGUAGES']) + '\n') answer = ask('Language(s) to use', 'en') langs = [i.strip().lower().replace('-', '_') for i in answer.split(',')] for partial, full in LEGAL_VALUES['_TRANSLATIONS_WITH_COUNTRY_SPECIFIERS'].items(): if partial in langs: langs[langs.index(partial)] = full print("NOTICE: Assuming '{0}' instead of '{1}'.".format(full, partial)) default = langs.pop(0) SAMPLE_CONF['DEFAULT_LANG'] = default # format_default_translations_config() is intelligent enough to # return the current value if there are no additional languages. SAMPLE_CONF['TRANSLATIONS'] = format_default_translations_config(langs) # Get messages for navigation_links. In order to do this, we need # to generate a throwaway TRANSLATIONS dict. tr = {default: ''} for l in langs: tr[l] = './' + l # Assuming that base contains all the locales, and that base does # not inherit from anywhere. try: messages = load_messages(['base'], tr, default) SAMPLE_CONF['NAVIGATION_LINKS'] = format_navigation_links(langs, default, messages) except nikola.utils.LanguageNotFoundError as e: print(" ERROR: the language '{0}' is not supported.".format(e.lang)) print(" Are you sure you spelled the name correctly? Names are case-sensitive and need to be reproduced as-is (complete with the country specifier, if any).") print("\nType '?' (a question mark, sans quotes) to list available languages.") lhandler(default, toconf, show_header=False) def tzhandler(default, toconf): print("\nPlease choose the correct time zone for your blog. Nikola uses the tz database.") print("You can find your time zone here:") print("http://en.wikipedia.org/wiki/List_of_tz_database_time_zones") print("") answered = False while not answered: try: lz = get_localzone() except: lz = None answer = ask('Time zone', lz if lz else "UTC") tz = dateutil.tz.gettz(answer) if tz is None: print(" WARNING: Time zone not found. Searching list of time zones for a match.") zonesfile = tarfile.open(fileobj=dateutil.zoneinfo.getzoneinfofile_stream()) zonenames = [zone for zone in zonesfile.getnames() if answer.lower() in zone.lower()] if len(zonenames) == 1: tz = dateutil.tz.gettz(zonenames[0]) answer = zonenames[0] print(" Picking '{0}'.".format(answer)) elif len(zonenames) > 1: print(" The following time zones match your query:") print(' ' + '\n '.join(zonenames)) continue if tz is not None: time = datetime.datetime.now(tz).strftime('%H:%M:%S') print(" Current time in {0}: {1}".format(answer, time)) answered = ask_yesno("Use this time zone?", True) else: print(" ERROR: No matches found. Please try again.") SAMPLE_CONF['TIMEZONE'] = answer def chandler(default, toconf): print("You can configure comments now. Type '?' (a question mark, sans quotes) to list available comment systems. If you do not want any comments, just leave the field blank.") answer = ask('Comment system', '') while answer.strip() == '?': print('\n# Available comment systems:') print(SAMPLE_CONF['_SUPPORTED_COMMENT_SYSTEMS']) print('') answer = ask('Comment system', '') while answer and answer not in LEGAL_VALUES['COMMENT_SYSTEM']: if answer != '?': print(' ERROR: Nikola does not know this comment system.') print('\n# Available comment systems:') print(SAMPLE_CONF['_SUPPORTED_COMMENT_SYSTEMS']) print('') answer = ask('Comment system', '') SAMPLE_CONF['COMMENT_SYSTEM'] = answer SAMPLE_CONF['COMMENT_SYSTEM_ID'] = '' if answer: print("You need to provide the site identifier for your comment system. Consult the Nikola manual for details on what the value should be. (you can leave it empty and come back later)") answer = ask('Comment system site identifier', '') SAMPLE_CONF['COMMENT_SYSTEM_ID'] = answer STORAGE = {'target': target} questions = [ ('Questions about the site', None, None, None), # query, default, toconf, destination ('Destination', None, False, '!target'), ('Site title', 'My Nikola Site', True, 'BLOG_TITLE'), ('Site author', 'Nikola Tesla', True, 'BLOG_AUTHOR'), ('Site author\'s e-mail', '*****@*****.**', True, 'BLOG_EMAIL'), ('Site description', 'This is a demo site for Nikola.', True, 'BLOG_DESCRIPTION'), ('Site URL', 'http://getnikola.com/', True, 'SITE_URL'), ('Questions about languages and locales', None, None, None), (lhandler, None, True, True), (tzhandler, None, True, True), ('Questions about comments', None, None, None), (chandler, None, True, True), ] print("Creating Nikola Site") print("====================\n") print("This is Nikola v{0}. We will now ask you a few easy questions about your new site.".format(nikola.__version__)) print("If you do not want to answer and want to go with the defaults instead, simply restart with the `-q` parameter.") for query, default, toconf, destination in questions: if target and destination == '!target': # Skip the destination question if we know it already pass else: if default is toconf is destination is None: print('--- {0} ---'.format(query)) elif destination is True: query(default, toconf) else: answer = ask(query, default) try: answer = answer.decode('utf-8') except (AttributeError, UnicodeDecodeError): pass if toconf: SAMPLE_CONF[destination] = answer if destination == '!target': while not answer: print(' ERROR: you need to specify a target directory.\n') answer = ask(query, default) STORAGE['target'] = answer print("\nThat's it, Nikola is now configured. Make sure to edit conf.py to your liking.") print("If you are looking for themes and addons, check out http://themes.getnikola.com/ and http://plugins.getnikola.com/.") print("Have fun!") return STORAGE
def ask_questions(target, demo=False): """Ask some questions about Nikola.""" def urlhandler(default, toconf): answer = ask('Site URL', 'https://example.com/') try: answer = answer.decode('utf-8') except (AttributeError, UnicodeDecodeError): pass if not answer.startswith(u'http'): print( " ERROR: You must specify a protocol (http or https).") urlhandler(default, toconf) return if not answer.endswith('/'): print(" The URL does not end in '/' -- adding it.") answer += '/' dst_url = urlsplit(answer) try: dst_url.netloc.encode('ascii') except (UnicodeEncodeError, UnicodeDecodeError): # The IDN contains characters beyond ASCII. We must convert it # to Punycode. (Issue #1644) nl = dst_url.netloc.encode('idna') answer = urlunsplit((dst_url.scheme, nl, dst_url.path, dst_url.query, dst_url.fragment)) print(" Converting to Punycode:", answer) SAMPLE_CONF['SITE_URL'] = answer def prettyhandler(default, toconf): SAMPLE_CONF['PRETTY_URLS'] = ask_yesno( 'Enable pretty URLs (/page/ instead of /page.html) that don\'t need web server configuration?', default=True) SAMPLE_CONF['STRIP_INDEXES'] = SAMPLE_CONF['PRETTY_URLS'] def lhandler(default, toconf, show_header=True): if show_header: print( "We will now ask you to provide the list of languages you want to use." ) print( "Please list all the desired languages, comma-separated, using ISO 639-1 codes. The first language will be used as the default." ) print( "Type '?' (a question mark, sans quotes) to list available languages." ) answer = ask('Language(s) to use', 'en') while answer.strip() == '?': print('\n# Available languages:') try: print(SAMPLE_CONF['_SUPPORTED_LANGUAGES'] + '\n') except UnicodeEncodeError: # avoid Unicode characters in supported language names print( unidecode.unidecode( SAMPLE_CONF['_SUPPORTED_LANGUAGES']) + '\n') answer = ask('Language(s) to use', 'en') langs = [ i.strip().lower().replace('-', '_') for i in answer.split(',') ] for partial, full in LEGAL_VALUES[ '_TRANSLATIONS_WITH_COUNTRY_SPECIFIERS'].items(): if partial in langs: langs[langs.index(partial)] = full print("NOTICE: Assuming '{0}' instead of '{1}'.".format( full, partial)) default = langs.pop(0) SAMPLE_CONF['DEFAULT_LANG'] = default # format_default_translations_config() is intelligent enough to # return the current value if there are no additional languages. SAMPLE_CONF['TRANSLATIONS'] = format_default_translations_config( langs) # Get messages for navigation_links. In order to do this, we need # to generate a throwaway TRANSLATIONS dict. tr = {default: ''} for l in langs: tr[l] = './' + l # Assuming that base contains all the locales, and that base does # not inherit from anywhere. try: messages = load_messages(['base'], tr, default, themes_dirs=['themes']) SAMPLE_CONF['NAVIGATION_LINKS'] = format_navigation_links( langs, default, messages, SAMPLE_CONF['STRIP_INDEXES']) except nikola.utils.LanguageNotFoundError as e: print(" ERROR: the language '{0}' is not supported.".format( e.lang)) print( " Are you sure you spelled the name correctly? Names are case-sensitive and need to be reproduced as-is (complete with the country specifier, if any)." ) print( "\nType '?' (a question mark, sans quotes) to list available languages." ) lhandler(default, toconf, show_header=False) def tzhandler(default, toconf): print( "\nPlease choose the correct time zone for your blog. Nikola uses the tz database." ) print("You can find your time zone here:") print( "https://en.wikipedia.org/wiki/List_of_tz_database_time_zones") print("") answered = False while not answered: try: lz = get_localzone() except: lz = None answer = ask('Time zone', lz if lz else "UTC") tz = dateutil.tz.gettz(answer) if tz is None: print( " WARNING: Time zone not found. Searching list of time zones for a match." ) zonesfile = tarfile.open( fileobj=dateutil.zoneinfo.getzoneinfofile_stream()) zonenames = [ zone for zone in zonesfile.getnames() if answer.lower() in zone.lower() ] if len(zonenames) == 1: tz = dateutil.tz.gettz(zonenames[0]) answer = zonenames[0] print(" Picking '{0}'.".format(answer)) elif len(zonenames) > 1: print(" The following time zones match your query:") print(' ' + '\n '.join(zonenames)) continue if tz is not None: time = datetime.datetime.now(tz).strftime('%H:%M:%S') print(" Current time in {0}: {1}".format(answer, time)) answered = ask_yesno("Use this time zone?", True) else: print(" ERROR: No matches found. Please try again.") SAMPLE_CONF['TIMEZONE'] = answer def chandler(default, toconf): print( "You can configure comments now. Type '?' (a question mark, sans quotes) to list available comment systems. If you do not want any comments, just leave the field blank." ) answer = ask('Comment system', '') while answer.strip() == '?': print('\n# Available comment systems:') print(SAMPLE_CONF['_SUPPORTED_COMMENT_SYSTEMS']) print('') answer = ask('Comment system', '') while answer and answer not in LEGAL_VALUES['COMMENT_SYSTEM']: if answer != '?': print( ' ERROR: Nikola does not know this comment system.') print('\n# Available comment systems:') print(SAMPLE_CONF['_SUPPORTED_COMMENT_SYSTEMS']) print('') answer = ask('Comment system', '') SAMPLE_CONF['COMMENT_SYSTEM'] = answer SAMPLE_CONF['COMMENT_SYSTEM_ID'] = '' if answer: print( "You need to provide the site identifier for your comment system. Consult the Nikola manual for details on what the value should be. (you can leave it empty and come back later)" ) answer = ask('Comment system site identifier', '') SAMPLE_CONF['COMMENT_SYSTEM_ID'] = answer STORAGE = {'target': target} questions = [ ('Questions about the site', None, None, None), # query, default, toconf, destination ('Destination', None, False, '!target'), ('Site title', 'My Nikola Site', True, 'BLOG_TITLE'), ('Site author', 'Nikola Tesla', True, 'BLOG_AUTHOR'), ('Site author\'s e-mail', '*****@*****.**', True, 'BLOG_EMAIL'), ('Site description', 'This is a demo site for Nikola.', True, 'BLOG_DESCRIPTION'), (urlhandler, None, True, True), (prettyhandler, None, True, True), ('Questions about languages and locales', None, None, None), (lhandler, None, True, True), (tzhandler, None, True, True), ('Questions about comments', None, None, None), (chandler, None, True, True), ] print("Creating Nikola Site") print("====================\n") print( "This is Nikola v{0}. We will now ask you a few easy questions about your new site." .format(nikola.__version__)) print( "If you do not want to answer and want to go with the defaults instead, simply restart with the `-q` parameter." ) for query, default, toconf, destination in questions: if target and destination == '!target' and test_destination( target, demo): # Skip the destination question if we know it already pass else: if default is toconf is destination is None: print('--- {0} ---'.format(query)) elif destination is True: query(default, toconf) else: answer = ask(query, default) try: answer = answer.decode('utf-8') except (AttributeError, UnicodeDecodeError): pass if toconf: SAMPLE_CONF[destination] = answer if destination == '!target': while not answer or not test_destination(answer, demo): if not answer: print( ' ERROR: you need to specify a target directory.\n' ) answer = ask(query, default) STORAGE['target'] = answer print( "\nThat's it, Nikola is now configured. Make sure to edit conf.py to your liking." ) print( "If you are looking for themes and addons, check out https://themes.getnikola.com/ and https://plugins.getnikola.com/." ) print("Have fun!") return STORAGE
def _execute(self, options, args): """Create a new post or page.""" global LOGGER compiler_names = [ p.name for p in self.site.plugin_manager.getPluginsOfCategory( "PageCompiler") ] if len(args) > 1: print(self.help()) return False elif args: path = args[0] else: path = None # Even though stuff was split into `new_page`, it’s easier to do it # here not to duplicate the code. is_page = options.get('is_page', False) is_post = not is_page content_type = 'page' if is_page else 'post' title = options['title'] or None author = options['author'] or '' tags = options['tags'] onefile = options['onefile'] twofile = options['twofile'] import_file = options['import'] if is_page: LOGGER = PAGELOGGER else: LOGGER = POSTLOGGER if twofile: onefile = False if not onefile and not twofile: onefile = self.site.config.get('ONE_FILE_POSTS', True) content_format = options['content_format'] if not content_format: # Issue #400 content_format = get_default_compiler( is_post, self.site.config['COMPILERS'], self.site.config['post_pages']) if content_format not in compiler_names: LOGGER.error("Unknown {0} format {1}".format( content_type, content_format)) return compiler_plugin = self.site.plugin_manager.getPluginByName( content_format, "PageCompiler").plugin_object # Guess where we should put this entry = filter_post_pages(content_format, is_post, self.site.config['COMPILERS'], self.site.config['post_pages']) if import_file: print("Importing Existing {xx}".format(xx=content_type.title())) print("-----------------------\n") else: print("Creating New {xx}".format(xx=content_type.title())) print("-----------------\n") if title is not None: print("Title:", title) else: while not title: title = utils.ask('Title') if isinstance(title, utils.bytes_str): try: title = title.decode(sys.stdin.encoding) except (AttributeError, TypeError): # for tests title = title.decode('utf-8') title = title.strip() if not path: slug = utils.slugify(title) else: if isinstance(path, utils.bytes_str): try: path = path.decode(sys.stdin.encoding) except (AttributeError, TypeError): # for tests path = path.decode('utf-8') slug = utils.slugify(os.path.splitext(os.path.basename(path))[0]) if isinstance(author, utils.bytes_str): try: author = author.decode(sys.stdin.encoding) except (AttributeError, TypeError): # for tests author = author.decode('utf-8') # Calculate the date to use for the content schedule = options['schedule'] or self.site.config['SCHEDULE_ALL'] rule = self.site.config['SCHEDULE_RULE'] self.site.scan_posts() timeline = self.site.timeline last_date = None if not timeline else timeline[0].date date = get_date(schedule, rule, last_date, self.site.tzinfo, self.site.config['FORCE_ISO8601']) data = { 'title': title, 'slug': slug, 'date': date, 'tags': tags, 'link': '', 'description': '', 'type': 'text', } output_path = os.path.dirname(entry[0]) meta_path = os.path.join(output_path, slug + ".meta") pattern = os.path.basename(entry[0]) suffix = pattern[1:] if not path: txt_path = os.path.join(output_path, slug + suffix) else: txt_path = path if (not onefile and os.path.isfile(meta_path)) or \ os.path.isfile(txt_path): LOGGER.error("The title already exists!") exit(8) d_name = os.path.dirname(txt_path) utils.makedirs(d_name) metadata = {} if author: metadata['author'] = author metadata.update(self.site.config['ADDITIONAL_METADATA']) data.update(metadata) # Override onefile if not really supported. if not compiler_plugin.supports_onefile and onefile: onefile = False LOGGER.warn('This compiler does not support one-file posts.') if import_file: with io.open(import_file, 'r', encoding='utf-8') as fh: content = fh.read() else: # ipynb's create_post depends on this exact string, take care # if you're changing it content = "Write your {0} here.".format( 'page' if is_page else 'post') compiler_plugin.create_post(txt_path, content=content, onefile=onefile, title=title, slug=slug, date=date, tags=tags, is_page=is_page, **metadata) event = dict(path=txt_path) if not onefile: # write metadata file with io.open(meta_path, "w+", encoding="utf8") as fd: fd.write(utils.write_metadata(data)) LOGGER.info("Your {0}'s metadata is at: {1}".format( content_type, meta_path)) event['meta_path'] = meta_path LOGGER.info("Your {0}'s text is at: {1}".format( content_type, txt_path)) signal('new_' + content_type).send(self, **event) if options['edit']: editor = os.getenv('EDITOR', '').split() to_run = editor + [txt_path] if not onefile: to_run.append(meta_path) if editor: subprocess.call(to_run) else: LOGGER.error( '$EDITOR not set, cannot edit the post. Please do it manually.' )
def ask_questions(target): """Ask some questions about Nikola.""" def urlhandler(default, toconf): answer = ask("Site URL", "https://example.com/") try: answer = answer.decode("utf-8") except (AttributeError, UnicodeDecodeError): pass if not answer.startswith("http"): print(" ERROR: You must specify a protocol (http or https).") urlhandler(default, toconf) return if not answer.endswith("/"): print(" The URL does not end in '/' -- adding it.") answer += "/" dst_url = urlsplit(answer) try: dst_url.netloc.encode("ascii") except (UnicodeEncodeError, UnicodeDecodeError): # The IDN contains characters beyond ASCII. We must convert it # to Punycode. (Issue #1644) nl = dst_url.netloc.encode("idna") answer = urlunsplit((dst_url.scheme, nl, dst_url.path, dst_url.query, dst_url.fragment)) print(" Converting to Punycode:", answer) SAMPLE_CONF["SITE_URL"] = answer def prettyhandler(default, toconf): SAMPLE_CONF["PRETTY_URLS"] = ask_yesno( "Enable pretty URLs (/page/ instead of /page.html) that don't need web server configuration?", default=True, ) SAMPLE_CONF["STRIP_INDEXES"] = SAMPLE_CONF["PRETTY_URLS"] def lhandler(default, toconf, show_header=True): if show_header: print("We will now ask you to provide the list of languages you want to use.") print( "Please list all the desired languages, comma-separated, using ISO 639-1 codes. The first language will be used as the default." ) print("Type '?' (a question mark, sans quotes) to list available languages.") answer = ask("Language(s) to use", "en") while answer.strip() == "?": print("\n# Available languages:") try: print(SAMPLE_CONF["_SUPPORTED_LANGUAGES"] + "\n") except UnicodeEncodeError: # avoid Unicode characters in supported language names print(unidecode.unidecode(SAMPLE_CONF["_SUPPORTED_LANGUAGES"]) + "\n") answer = ask("Language(s) to use", "en") langs = [i.strip().lower().replace("-", "_") for i in answer.split(",")] for partial, full in LEGAL_VALUES["_TRANSLATIONS_WITH_COUNTRY_SPECIFIERS"].items(): if partial in langs: langs[langs.index(partial)] = full print("NOTICE: Assuming '{0}' instead of '{1}'.".format(full, partial)) default = langs.pop(0) SAMPLE_CONF["DEFAULT_LANG"] = default # format_default_translations_config() is intelligent enough to # return the current value if there are no additional languages. SAMPLE_CONF["TRANSLATIONS"] = format_default_translations_config(langs) # Get messages for navigation_links. In order to do this, we need # to generate a throwaway TRANSLATIONS dict. tr = {default: ""} for l in langs: tr[l] = "./" + l # Assuming that base contains all the locales, and that base does # not inherit from anywhere. try: messages = load_messages(["base"], tr, default) SAMPLE_CONF["NAVIGATION_LINKS"] = format_navigation_links( langs, default, messages, SAMPLE_CONF["STRIP_INDEXES"] ) except nikola.utils.LanguageNotFoundError as e: print(" ERROR: the language '{0}' is not supported.".format(e.lang)) print( " Are you sure you spelled the name correctly? Names are case-sensitive and need to be reproduced as-is (complete with the country specifier, if any)." ) print("\nType '?' (a question mark, sans quotes) to list available languages.") lhandler(default, toconf, show_header=False) def tzhandler(default, toconf): print("\nPlease choose the correct time zone for your blog. Nikola uses the tz database.") print("You can find your time zone here:") print("https://en.wikipedia.org/wiki/List_of_tz_database_time_zones") print("") answered = False while not answered: try: lz = get_localzone() except: lz = None answer = ask("Time zone", lz if lz else "UTC") tz = dateutil.tz.gettz(answer) if tz is None: print(" WARNING: Time zone not found. Searching list of time zones for a match.") zonesfile = tarfile.open(fileobj=dateutil.zoneinfo.getzoneinfofile_stream()) zonenames = [zone for zone in zonesfile.getnames() if answer.lower() in zone.lower()] if len(zonenames) == 1: tz = dateutil.tz.gettz(zonenames[0]) answer = zonenames[0] print(" Picking '{0}'.".format(answer)) elif len(zonenames) > 1: print(" The following time zones match your query:") print(" " + "\n ".join(zonenames)) continue if tz is not None: time = datetime.datetime.now(tz).strftime("%H:%M:%S") print(" Current time in {0}: {1}".format(answer, time)) answered = ask_yesno("Use this time zone?", True) else: print(" ERROR: No matches found. Please try again.") SAMPLE_CONF["TIMEZONE"] = answer def chandler(default, toconf): print( "You can configure comments now. Type '?' (a question mark, sans quotes) to list available comment systems. If you do not want any comments, just leave the field blank." ) answer = ask("Comment system", "") while answer.strip() == "?": print("\n# Available comment systems:") print(SAMPLE_CONF["_SUPPORTED_COMMENT_SYSTEMS"]) print("") answer = ask("Comment system", "") while answer and answer not in LEGAL_VALUES["COMMENT_SYSTEM"]: if answer != "?": print(" ERROR: Nikola does not know this comment system.") print("\n# Available comment systems:") print(SAMPLE_CONF["_SUPPORTED_COMMENT_SYSTEMS"]) print("") answer = ask("Comment system", "") SAMPLE_CONF["COMMENT_SYSTEM"] = answer SAMPLE_CONF["COMMENT_SYSTEM_ID"] = "" if answer: print( "You need to provide the site identifier for your comment system. Consult the Nikola manual for details on what the value should be. (you can leave it empty and come back later)" ) answer = ask("Comment system site identifier", "") SAMPLE_CONF["COMMENT_SYSTEM_ID"] = answer STORAGE = {"target": target} questions = [ ("Questions about the site", None, None, None), # query, default, toconf, destination ("Destination", None, False, "!target"), ("Site title", "My Nikola Site", True, "BLOG_TITLE"), ("Site author", "Nikola Tesla", True, "BLOG_AUTHOR"), ("Site author's e-mail", "*****@*****.**", True, "BLOG_EMAIL"), ("Site description", "This is a demo site for Nikola.", True, "BLOG_DESCRIPTION"), (urlhandler, None, True, True), (prettyhandler, None, True, True), ("Questions about languages and locales", None, None, None), (lhandler, None, True, True), (tzhandler, None, True, True), ("Questions about comments", None, None, None), (chandler, None, True, True), ] print("Creating Nikola Site") print("====================\n") print( "This is Nikola v{0}. We will now ask you a few easy questions about your new site.".format( nikola.__version__ ) ) print( "If you do not want to answer and want to go with the defaults instead, simply restart with the `-q` parameter." ) for query, default, toconf, destination in questions: if target and destination == "!target": # Skip the destination question if we know it already pass else: if default is toconf is destination is None: print("--- {0} ---".format(query)) elif destination is True: query(default, toconf) else: answer = ask(query, default) try: answer = answer.decode("utf-8") except (AttributeError, UnicodeDecodeError): pass if toconf: SAMPLE_CONF[destination] = answer if destination == "!target": while not answer: print(" ERROR: you need to specify a target directory.\n") answer = ask(query, default) STORAGE["target"] = answer print("\nThat's it, Nikola is now configured. Make sure to edit conf.py to your liking.") print( "If you are looking for themes and addons, check out https://themes.getnikola.com/ and https://plugins.getnikola.com/." ) print("Have fun!") return STORAGE
def ask_questions(target): """Ask some questions about Nikola.""" def lhandler(default, toconf, show_header=True): if show_header: print("We will now ask you to provide the list of languages you want to use.") print("Please list all the desired languages, comma-separated, using ISO 639-1 codes. The first language will be used as the default.") print("Type '?' (a question mark, sans quotes) to list available languages.") answer = ask('Language(s) to use', 'en') while answer.strip() == '?': print('\n# Available languages:') print(SAMPLE_CONF['_SUPPORTED_LANGUAGES'] + '\n') answer = ask('Language(s) to use', 'en') langs = [i.strip().lower().replace('-', '_') for i in answer.split(',')] for partial, full in LEGAL_VALUES['_TRANSLATIONS_WITH_COUNTRY_SPECIFIERS'].items(): if partial in langs: langs[langs.index(partial)] = full print("NOTICE: Assuming '{0}' instead of '{1}'.".format(full, partial)) default = langs.pop(0) SAMPLE_CONF['DEFAULT_LANG'] = default # format_default_translations_config() is intelligent enough to # return the current value if there are no additional languages. SAMPLE_CONF['TRANSLATIONS'] = format_default_translations_config(langs) # Get messages for navigation_links. In order to do this, we need # to generate a throwaway TRANSLATIONS dict. tr = {default: ''} for l in langs: tr[l] = './' + l # Assuming that base contains all the locales, and that base does # not inherit from anywhere. try: messages = load_messages(['base'], tr, default) SAMPLE_CONF['NAVIGATION_LINKS'] = format_navigation_links(langs, default, messages) except nikola.utils.LanguageNotFoundError as e: print(" ERROR: the language '{0}' is not supported.".format(e.lang)) print(" Are you sure you spelled the name correctly? Names are case-sensitive and need to be reproduced as-is (complete with the country specifier, if any).") print("\nType '?' (a question mark, sans quotes) to list available languages.") lhandler(default, toconf, show_header=False) def tzhandler(default, toconf): print("\nPlease choose the correct time zone for your blog. Nikola uses the tz database.") print("You can find your time zone here:") print("http://en.wikipedia.org/wiki/List_of_tz_database_time_zones") print("") answered = False while not answered: answer = ask('Time zone', 'UTC') tz = dateutil.tz.gettz(answer) if tz is not None: time = datetime.datetime.now(tz).strftime('%H:%M:%S') print(" Current time in {0}: {1}".format(answer, time)) answered = ask_yesno("Use this time zone?", True) else: print(" ERROR: Time zone not found. Please try again. Time zones are case-sensitive.") SAMPLE_CONF['TIMEZONE'] = answer def chandler(default, toconf): print("You can configure comments now. Type '?' (a question mark, sans quotes) to list available comment systems. If you do not want any comments, just leave the field blank.") answer = ask('Comment system', '') while answer.strip() == '?': print('\n# Available comment systems:') print(SAMPLE_CONF['_SUPPORTED_COMMENT_SYSTEMS']) print('') answer = ask('Comment system', '') while answer and answer not in LEGAL_VALUES['COMMENT_SYSTEM']: if answer != '?': print(' ERROR: Nikola does not know this comment system.') print('\n# Available comment systems:') print(SAMPLE_CONF['_SUPPORTED_COMMENT_SYSTEMS']) print('') answer = ask('Comment system', '') SAMPLE_CONF['COMMENT_SYSTEM'] = answer SAMPLE_CONF['COMMENT_SYSTEM_ID'] = '' if answer: print("You need to provide the site identifier for your comment system. Consult the Nikola manual for details on what the value should be. (you can leave it empty and come back later)") answer = ask('Comment system site identifier', '') SAMPLE_CONF['COMMENT_SYSTEM_ID'] = answer STORAGE = {'target': target} questions = [ ('Questions about the site', None, None, None), # query, default, toconf, destination ('Destination', None, False, '!target'), ('Site title', 'My Nikola Site', True, 'BLOG_TITLE'), ('Site author', 'Nikola Tesla', True, 'BLOG_AUTHOR'), ('Site author’s e-mail', '*****@*****.**', True, 'BLOG_EMAIL'), ('Site description', 'This is a demo site for Nikola.', True, 'BLOG_DESCRIPTION'), ('Site URL', 'http://getnikola.com/', True, 'SITE_URL'), ('Questions about languages and locales', None, None, None), (lhandler, None, True, True), (tzhandler, None, True, True), ('Questions about comments', None, None, None), (chandler, None, True, True), ] print("Creating Nikola Site") print("====================\n") print("This is Nikola v{0}. We will now ask you a few easy questions about your new site.".format(nikola.__version__)) print("If you do not want to answer and want to go with the defaults instead, simply restart with the `-q` parameter.") for query, default, toconf, destination in questions: if target and destination == '!target': # Skip the destination question if we know it already pass else: if default is toconf is destination is None: print('--- {0} ---'.format(query)) elif destination is True: query(default, toconf) else: answer = ask(query, default) if toconf: SAMPLE_CONF[destination] = answer if destination == '!target': while not answer: print(' ERROR: you need to specify a target directory.\n') answer = ask(query, default) STORAGE['target'] = answer print("\nThat's it, Nikola is now configured. Make sure to edit conf.py to your liking.") print("If you are looking for themes and addons, check out http://themes.getnikola.com/ and http://plugins.getnikola.com/.") print("Have fun!") return STORAGE